Análisis de código estático de la colección de la biblioteca PMDK de Intel y errores que no son errores

PVS-Studio, PMDK


Se nos ofreció utilizar el analizador PVS-Studio para comprobar la colección de bibliotecas PMDK abiertas destinadas a desarrollar y depurar aplicaciones con soporte de memoria no volátil. De hecho, ¿por qué no? Además, este es un pequeño proyecto en C y C ++ con un tamaño de base de código total de aproximadamente 170 KLOC, excluidos los comentarios. Esto significa que revisar los resultados del análisis no llevará mucho tiempo y esfuerzo. Vamonos.



Para analizar el código fuente, se utilizará la herramienta PVS-Studio versión 7.08. Los lectores de nuestro blog, naturalmente, conocen nuestra herramienta desde hace mucho tiempo y no me detendré en ella. Para aquellos que vino a nosotros por primera vez, sugiero referirse al artículo " Cómo ver rápidamente las advertencias interesantes que el analizador PVS-Studio da para el código C y C ++? " Y probar una versión de prueba gratuita del analizador.



Esta vez miraré dentro del proyecto PMDK y les contaré los errores y deficiencias que noté. En mi sentimiento interior, no había muchos de ellos, lo que indica la buena calidad del código de este proyecto. De las cosas interesantes, podemos notar que se encontraron varios fragmentos del código incorrecto, que sin embargo funciona correctamente :). Lo que quiero decir se aclarará con la narración posterior.



En resumen, PMDK es una colección de bibliotecas y herramientas de código abierto diseñadas para simplificar el desarrollo, la depuración y la administración de aplicaciones habilitadas para memoria no volátil. Más detalles aquí: Introducción al PMDK . Fuentes aquí: pmdk .



Veamos qué errores y deficiencias puedo encontrar en él. Debo decir de inmediato que estaba lejos de estar siempre atento al analizar el informe y podría haberme perdido mucho. Por lo tanto, insto a los autores del proyecto a no guiarse exclusivamente por este artículo al corregir defectos, sino a verificar el código usted mismo. Y para escribir un artículo, lo que escribí, mirando la lista de advertencias, me bastará :).



Código incorrecto que funciona



Tamaño de la asignación de memoria



No es raro que los programadores dediquen tiempo a depurar código cuando el programa no se comporta como debería. Sin embargo, a veces hay situaciones en las que el programa funciona correctamente, pero el código contiene un error. El programador tiene suerte y el error no se manifiesta. En el proyecto PMDK, encontré varias situaciones tan interesantes a la vez y, por lo tanto, decidí juntarlas en un capítulo separado.



int main(int argc, char *argv[])
{
  ....
  struct pool *pop = malloc(sizeof(pop));
  ....
}


Advertencia de PVS-Studio: V568 Es extraño que el operador 'sizeof ()' evalúe el tamaño de un puntero a una clase, pero no el tamaño del objeto de clase 'pop'. util_ctl.c 717



Un error tipográfico clásico que hace que se asigne una cantidad incorrecta de memoria. El operador sizeof devolverá no el tamaño de la estructura, sino el tamaño del puntero a esta estructura. La opción correcta sería:



struct pool *pop = malloc(sizeof(pool));


o



struct pool *pop = malloc(sizeof(*pop));


Sin embargo, este código escrito incorrectamente funciona muy bien. El punto es que la estructura del grupo contiene exactamente un puntero:



struct pool {
  struct ctl *ctl;
};


Resulta que la estructura ocupa exactamente tanto como el puntero. Las cosas son buenas.



Longitud de la línea



Pasemos al siguiente caso, donde el error se cometió nuevamente usando el operador sizeof .



typedef void *(*pmem2_memcpy_fn)(void *pmemdest, const void *src, size_t len,
    unsigned flags);

static const char *initial_state = "No code.";

static int
test_rwx_prot_map_priv_do_execute(const struct test_case *tc,
  int argc, char *argv[])
{
  ....
  char *addr_map = pmem2_map_get_address(map);
  map->memcpy_fn(addr_map, initial_state, sizeof(initial_state), 0);
  ....
}


Advertencia de PVS-Studio: V579 [CWE-687] La ​​función memcpy_fn recibe el puntero y su tamaño como argumentos. Posiblemente sea un error. Examine el tercer argumento. pmem2_map_prot.c 513



Se utiliza un puntero a una función de copia especial para copiar una cadena. Preste atención a la llamada de esta función, o más bien a su tercer argumento.



El programador asume que el operador sizeof calculará el tamaño del literal de cadena. Pero, de hecho, el tamaño del puntero se vuelve a calcular.



Afortunadamente, la cadena consta de 8 caracteres y su tamaño es el mismo que el del puntero si se crea una aplicación de 64 bits. Como resultado, los 8 caracteres de la cadena "Sin código". se copiará correctamente.



De hecho, la situación es aún más complicada e interesante. La interpretación de este error depende de si desea copiar el terminal cero o no. Consideremos dos escenarios.



Escenario 1. Era necesario copiar el terminal cero. Entonces me equivoco, y esto no es en absoluto un error inofensivo que no se manifiesta. No se copiaron 9 bytes, sino solo 8. No hay cero terminal y las consecuencias no se pueden predecir. En este caso, el código se puede corregir cambiando la definición de la cadena constante initial_state de la siguiente manera:



static const char initial_state [] = "No code.";


Ahora el valor de sizeof (initial_state) es 9.



Escenario 2. La terminal cero no es necesaria en absoluto. Por ejemplo, a continuación puede ver la siguiente línea de código:



UT_ASSERTeq(memcmp(addr_map, initial_state, strlen(initial_state)), 0);


Como puede ver, la función strlen devolverá el valor 8 y el terminal cero no se incluye en la comparación. Entonces es suerte y todo va bien.



Cambio de bit



El siguiente ejemplo está relacionado con una operación de desplazamiento bit a bit.



static int
clo_parse_single_uint(struct benchmark_clo *clo, const char *arg, void *ptr)
{
  ....
  uint64_t tmax = ~0 >> (64 - 8 * clo->type_uint.size);
  ....
}


Advertencia de PVS-Studio: V610 [CWE-758] Comportamiento no especificado. Compruebe el operador de turno '>>'. El operando izquierdo '~ 0' es negativo. clo.cpp 205



El resultado de mover un valor negativo a la derecha depende de la implementación del compilador. Por lo tanto, aunque dicho código puede funcionar correctamente y como se esperaba en todos los modos de compilación de aplicaciones existentes actualmente, todavía es suerte.



Prioridad de operaciones



Y considere el último caso relacionado con la prioridad de operaciones.



#define BTT_CREATE_DEF_SIZE  (20 * 1UL << 20) /* 20 MB */


Advertencia de PVS-Studio: V634 [CWE-783] La prioridad de la operación '*' es mayor que la de la operación '<<'. Es posible que se deban usar paréntesis en la expresión. bttcreate.c 204



Para obtener una constante igual a 20 MB, el programador decidió hacer lo siguiente:



  • Desplazado 1 por 20 bits para obtener 1048576, es decir 1 MB.
  • Multiplica 1 MB por 20.


En otras palabras, el programador piensa que los cálculos se realizan así: (20 * (1UL << 20))



Pero de hecho, la prioridad del operador de multiplicación es mayor que la del operador de turno, y la expresión se evalúa así: ((20 * 1UL) << 20).



De acuerdo, el programador no quería que la expresión se evaluara en esa secuencia. No tiene sentido multiplicar 20 por 1. Así que este es el caso cuando el código no funciona de la manera que el programador pretendía.



Pero este error no se manifestará de ninguna manera. No importa cómo escribas:



  • (20 * 1UL << 20)
  • (20 * (1UL << 20))
  • ((20 * 1UL) << 20)


¡El resultado es siempre el mismo ! El valor deseado es siempre 20971520 y el programa funciona perfectamente correctamente.



Otros errores



Paréntesis en el lugar equivocado



#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004

static void
enum_handles(int op)
{
  ....
  NTSTATUS status;
  while ((status = NtQuerySystemInformation(
      SystemExtendedHandleInformation,
      hndl_info, hi_size, &req_size)
        == STATUS_INFO_LENGTH_MISMATCH)) {
    hi_size = req_size + 4096;
    hndl_info = (PSYSTEM_HANDLE_INFORMATION_EX)REALLOC(hndl_info,
        hi_size);
  }
  UT_ASSERT(status >= 0);
  ....
}


Advertencia de PVS-Studio: V593 [CWE-783] Considere revisar la expresión del tipo 'A = B == C'. La expresión se calcula de la siguiente manera: 'A = (B == C)'. ut.c 641



Eche un vistazo aquí:



while ((status = NtQuerySystemInformation(....) == STATUS_INFO_LENGTH_MISMATCH))


El programador quería almacenar en la variable de estado el valor que devuelve la función NtQuerySystemInformation y luego compararlo con una constante.



Lo más probable es que el programador supiera que el operador de comparación (==) tiene una prioridad más alta que el operador de asignación (=) y, por lo tanto, se deben usar paréntesis. Pero se selló y los puso en el lugar equivocado. Como resultado, los paréntesis no ayudan de ninguna manera. Código correcto:



while ((status = NtQuerySystemInformation(....)) == STATUS_INFO_LENGTH_MISMATCH)


Debido a este error, la macro UT_ASSERT nunca funcionará. De hecho, la variable de estado siempre contiene el resultado de la comparación, es decir, falso (0) o verdadero (1). Por tanto, la condición ([0..1]> = 0) es siempre verdadera.



Pérdida de memoria potencial



static enum pocli_ret
pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp)
{
  char *input = strdup(in);
  if (!input)
    return POCLI_ERR_MALLOC;

  if (!oidp)
    return POCLI_ERR_PARS;
  ....
}


Advertencia de PVS-Studio: V773 [CWE-401] Se salió de la función sin soltar el puntero de 'entrada'. Es posible una pérdida de memoria. pmemobjcli.c 238



Si oidp resulta ser un puntero nulo, se perderá la copia de la cadena creada al llamar a la función strdup . Lo mejor sería posponer la verificación antes de asignar memoria:



static enum pocli_ret
pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp)
{
  if (!oidp)
    return POCLI_ERR_PARS;

  char *input = strdup(in);
  if (!input)
    return POCLI_ERR_MALLOC;
  ....
}


O puede liberar explícitamente la memoria:



static enum pocli_ret
pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp)
{
  char *input = strdup(in);
  if (!input)
    return POCLI_ERR_MALLOC;

  if (!oidp)
  {
    free(input);
    return POCLI_ERR_PARS;
  }
  ....
}


Desbordamiento potencial



typedef long long os_off_t;

void
do_memcpy(...., int dest_off, ....., size_t mapped_len, .....)
{
  ....
  LSEEK(fd, (os_off_t)(dest_off + (int)(mapped_len / 2)), SEEK_SET);
  ....
}


Advertencia de PVS-Studio: V1028 [CWE-190] Posible desbordamiento. Considere la posibilidad de emitir operandos, no el resultado. memcpy_common.c 62 No tiene sentido



emitir explícitamente el resultado de la adición al tipo os_off_t . Primero, no protege contra el desbordamiento potencial que puede ocurrir al agregar dos valores int . En segundo lugar, el resultado de la adición se habría expandido perfectamente implícitamente al tipo os_off_t . La conversión de tipos explícitos es simplemente redundante.



Creo que sería más correcto escribir así:



LSEEK(fd, dest_off + (os_off_t)(mapped_len) / 2, SEEK_SET);


Aquí, un valor sin signo del tipo size_t se convierte en un valor con signo (para que no haya advertencia del compilador). Y al mismo tiempo, definitivamente no se producirá un desbordamiento de la adición.



Protección contra desbordamiento incorrecta



static DWORD
get_rel_wait(const struct timespec *abstime)
{
  struct __timeb64 t;
  _ftime64_s(&t);
  time_t now_ms = t.time * 1000 + t.millitm;
  time_t ms = (time_t)(abstime->tv_sec * 1000 +
    abstime->tv_nsec / 1000000);

  DWORD rel_wait = (DWORD)(ms - now_ms);

  return rel_wait < 0 ? 0 : rel_wait;
}


Advertencia de PVS-Studio: V547 [CWE-570] La expresión 'rel_wait <0' siempre es falsa. El valor de tipo sin firmar nunca es <0. os_thread_windows.c 359



No tengo muy claro en qué situación debería protegerse el cheque, pero de todos modos no funciona. La variable rel_wait es de tipo DWORD sin firmar . Esto significa que la comparación rel_wait <0 no tiene sentido, ya que el resultado siempre es verdadero.



Falta de verificación de que la memoria se haya asignado correctamente



Comprobación de que la memoria se asigna se realiza mediante aserciones macros , que no hacen nada si se compila la versión de la aplicación. Entonces podemos decir que no se maneja la situación en la que las funciones malloc devuelven NULL . Ejemplo:



static void
remove_extra_node(TOID(struct tree_map_node) *node)
{
  ....
  unsigned char *new_key = (unsigned char *)malloc(new_key_size);
  assert(new_key != NULL);
  memcpy(new_key, D_RO(tmp)->key, D_RO(tmp)->key_size);
  ....
}


Advertencia de PVS-Studio: V575 [CWE-628] El puntero nulo potencial se pasa a la función 'memcpy'. Inspeccione el primer argumento. Verifique las líneas: 340, 338. rtree_map.c 340



En otra parte ni siquiera hay una afirmación :



static void
calc_pi_mt(void)
{
  ....
  HANDLE *workers = (HANDLE *) malloc(sizeof(HANDLE) * pending);
  for (i = 0; i < pending; ++i) {
    workers[i] = CreateThread(NULL, 0, calc_pi,
      &tasks[i], 0, NULL);
    if (workers[i] == NULL)
      break;
  }
  ....
}


Advertencia de PVS-Studio: V522 [CWE-690] Es posible que se eliminen las referencias de un posible puntero nulo 'trabajadores'. Líneas de comprobación: 126, 124. pi.c 126 Conté



al menos 37 fragmentos de código de este tipo. Así que no veo ninguna razón para enumerarlos todos en el artículo.



A primera vista, la falta de controles puede considerarse simplemente descuido y decir que se trata de un código con olor. No estoy de acuerdo con esta posición. Los programadores subestiman el peligro de no tener tales controles. Un puntero nulo no se manifiesta necesariamente de inmediato como un bloqueo del programa cuando se intenta eliminar la referencia. Las consecuencias pueden ser más extrañas y peligrosas, especialmente en programas de subprocesos múltiples. Para comprender con más detalle cuál es el problema y por qué se necesitan controles, recomiendo encarecidamente a todos que lean el artículo "¿Por qué es importante comprobar lo que devolvió la función malloc ".



Código olfativo



Cierre de llamada doble



static void
prepare_map(struct pmem2_map **map_ptr,
  struct pmem2_config *cfg, struct pmem2_source *src)
{
  ....
  HANDLE mh = CreateFileMapping(....);
  ....
  UT_ASSERTne(CloseHandle(mh), 0);
  ....
}


Advertencia de PVS-Studio: V586 [CWE-675] La función 'CloseHandle' se llama dos veces para la desasignación del mismo recurso. pmem2_map.c 76



Mirando este código y la advertencia de PVS-Studio, está claro que nada está claro. ¿Dónde puedo volver a llamar a CloseHandle ? Para encontrar la respuesta, veamos la implementación de la macro UT_ASSERTne .



#define UT_ASSERTne(lhs, rhs)\
  do {\
    /* See comment in UT_ASSERT. */\
    if (__builtin_constant_p(lhs) && __builtin_constant_p(rhs))\
      UT_ASSERT_COMPILE_ERROR_ON((lhs) != (rhs));\
    UT_ASSERTne_rt(lhs, rhs);\
  } while (0)


No quedó mucho más claro. ¿Qué es UT_ASSERT_COMPILE_ERROR_ON ? ¿Qué es UT_ASSERTne_rt ?



No abarrotaré el artículo con una descripción de cada macro y atormentaré al lector, obligándolo a insertar una macros en otras en su cabeza. Veamos de una vez la versión final del código abierto extraído del archivo preprocesado.



do {
  if (0 && 0) (void)((CloseHandle(mh)) != (0));
  ((void)(((CloseHandle(mh)) != (0)) ||
    (ut_fatal(".....", 76, __FUNCTION__, "......: %s (0x%llx) != %s (0x%llx)",
              "CloseHandle(mh)", (unsigned long long)(CloseHandle(mh)), "0",
              (unsigned long long)(0)), 0))); } while (0);


Eliminemos la condición siempre falsa (0 && 0) y, en general, todo lo irrelevante. Resulta:



((void)(((CloseHandle(mh)) != (0)) ||
  (ut_fatal(...., "assertion failure: %s (0x%llx) != %s (0x%llx)",
            ....., (unsigned long long)(CloseHandle(mh)), .... ), 0)));


La manija está cerrada. Si ocurre un error, se genera un mensaje de depuración y, para volver a obtener el código de error, se llama a CloseHandle para el mismo identificador no válido.



Errores, más o menos, y no. Dado que el identificador no es válido, entonces está bien que se llame dos veces a la función CloseHandle . Sin embargo, este código es inodoro. Sería más correcto ideológicamente llamar a la función solo una vez y guardar el estado que devolvió, luego, si es necesario, mostrar su valor en el mensaje.



Incoherencia de la interfaz de implementación (eliminación de const)



static int
status_push(PMEMpoolcheck *ppc, struct check_status *st, uint32_t question)
{
  ....
  } else {
    status_msg_info_and_question(st->msg);            // <=
    st->question = question;
    ppc->result = CHECK_RESULT_ASK_QUESTIONS;
    st->answer = PMEMPOOL_CHECK_ANSWER_EMPTY;
    PMDK_TAILQ_INSERT_TAIL(&ppc->data->questions, st, next);
  }
  ....
}


El analizador muestra el mensaje: V530 [CWE-252] Es necesario utilizar el valor de retorno de la función 'status_msg_info_and_question'. check_util.c 293



La razón es que la función status_msg_info_and_question , desde el punto de vista del analizador, no cambia el estado de los objetos externos a él, incluida la cadena constante pasada. Aquellos. la función simplemente calcula algo y devuelve el resultado. Y si es así, entonces es extraño no usar el resultado que devuelve esta función. Y, aunque el analizador se equivoca esta vez, apunta a un código con olor. Veamos cómo funciona la función llamada status_msg_info_and_question .



static inline int
status_msg_info_and_question(const char *msg)
{
  char *sep = strchr(msg, MSG_SEPARATOR);
  if (sep) {
    *sep = ' ';
    return 0;
  }
  return -1;
}


Al llamar a la función strchr , se produce una eliminación implícita de la constante. El caso es que en C se declara así:



char * strchr ( const char *, int );


No es la mejor solución. Pero el lenguaje C es lo que es :).



El analizador se confundió y no entendió que la cadena pasada realmente está cambiando. Si es así, el valor de retorno no es lo más importante y no puede usarlo.



Sin embargo, aunque el analizador está confundido, apunta a un código con olor. Lo que confunde al analizador también puede confundir a la persona que mantiene el código. Sería mejor declarar la función de manera más honesta eliminando const :



static inline int
status_msg_info_and_question(char *msg)
{
  char *sep = strchr(msg, MSG_SEPARATOR);
  if (sep) {
    *sep = ' ';
    return 0;
  }
  return -1;
}


Entonces, las intenciones son inmediatamente más claras y el analizador se quedará en silencio.



Código demasiado complicado



static struct memory_block
heap_coalesce(struct palloc_heap *heap,
  const struct memory_block *blocks[], int n)
{
  struct memory_block ret = MEMORY_BLOCK_NONE;

  const struct memory_block *b = NULL;
  ret.size_idx = 0;
  for (int i = 0; i < n; ++i) {
    if (blocks[i] == NULL)
      continue;
    b = b ? b : blocks[i];
    ret.size_idx += blocks[i] ? blocks[i]->size_idx : 0;
  }
  ....
}


Advertencia de PVS-Studio: V547 [CWE-571] La expresión 'bloques [i]' siempre es verdadera. heap.c 1054



Si bloquea [i] == NULL , entonces se activará la instrucción continue y el ciclo iniciará la siguiente iteración. Por tanto, volver a comprobar los bloques de elementos [i] no tiene sentido y el operador ternario es superfluo. El código se puede simplificar:



....
for (int i = 0; i < n; ++i) {
  if (blocks[i] == NULL)
    continue;
  b = b ? b : blocks[i];
  ret.size_idx += blocks[i]->size_idx;
}
....


Uso sospechoso de un puntero nulo



void win_mmap_fini(void)
{
  ....
  if (mt->BaseAddress != NULL)
    UnmapViewOfFile(mt->BaseAddress);
  size_t release_size =
    (char *)mt->EndAddress - (char *)mt->BaseAddress;
  void *release_addr = (char *)mt->BaseAddress + mt->FileLen;
  mmap_unreserve(release_addr, release_size - mt->FileLen);
  ....
}


Advertencia de PVS-Studio: V1004 [CWE-119] El puntero '(char *) mt-> BaseAddress' se usó de manera insegura después de que se verificara con nullptr. Líneas de verificación: 226, 235. win_mmap.c 235



El puntero mt-> BaseAddress puede ser nulo, como lo demuestra la verificación:



if (mt->BaseAddress != NULL)


Sin embargo, debajo de este puntero ya se usa en operaciones aritméticas sin verificación. Por ejemplo, aquí:



size_t release_size =
  (char *)mt->EndAddress - (char *)mt->BaseAddress;


Se recibirá algún valor entero grande, que en realidad es el valor del puntero mt-> EndAddress . Puede que esto no sea un error, pero todo parece muy sospechoso y me parece que el código debería volver a comprobarse. El olor radica en el hecho de que el código es incomprensible y claramente carece de comentarios explicativos.



Nombres cortos de variables globales



Creo que el código huele mal si contiene variables globales con nombres cortos. Es fácil de sellar y usar accidentalmente no una variable local, sino global en alguna función. Ejemplo:



static struct critnib *c;


Advertencias de PVS-Studio para tales variables:



  • V707 Dar nombres cortos a variables globales se considera una mala práctica. Se sugiere cambiar el nombre de la variable 'ri'. map.c 131
  • V707 Dar nombres cortos a variables globales se considera una mala práctica. Se sugiere cambiar el nombre de la variable 'c'. obj_critnib_mt.c 56
  • V707 Dar nombres cortos a variables globales se considera una mala práctica. Se sugiere cambiar el nombre de la variable 'Id'. obj_list.h 68
  • V707 Dar nombres cortos a variables globales se considera una mala práctica. Se sugiere cambiar el nombre de la variable 'Id'. obj_list.c 34


Extraño



PVS-Studio: código extraño en una función


El código más extraño que encontré está en la función do_memmove . El analizador dio dos resultados positivos, que indican errores muy graves o que simplemente no entiendo lo que quiero decir. Dado que el código es muy extraño, decidí considerar las advertencias emitidas en una sección separada del artículo. Entonces, la primera advertencia se emite aquí.



void
do_memmove(char *dst, char *src, const char *file_name,
    size_t dest_off, size_t src_off, size_t bytes,
    memmove_fn fn, unsigned flags, persist_fn persist)
{
  ....
  /* do the same using regular memmove and verify that buffers match */
  memmove(dstshadow + dest_off, dstshadow + dest_off, bytes / 2);
  verify_contents(file_name, 0, dstshadow, dst, bytes);
  verify_contents(file_name, 1, srcshadow, src, bytes);
  ....
}


Advertencia de PVS-Studio: V549 [CWE-688] El primer argumento de la función 'memmove' es igual al segundo argumento. memmove_common.c 71



Tenga en cuenta que el primer y segundo argumento de la función son iguales. Por tanto, la función no hace nada de hecho. Qué opciones me vienen a la mente:



  • Quería "tocar" el bloque de memoria. Pero, ¿sucederá esto en realidad? ¿El compilador de optimización eliminará el código que copia el bloque de memoria en sí mismo?
  • Esta es una especie de prueba unitaria para la función memmove .
  • El código contiene un error tipográfico.


Y aquí hay un fragmento igualmente extraño en la misma función:



void
do_memmove(char *dst, char *src, const char *file_name,
    size_t dest_off, size_t src_off, size_t bytes,
    memmove_fn fn, unsigned flags, persist_fn persist)
{
  ....
  /* do the same using regular memmove and verify that buffers match */
  memmove(dstshadow + dest_off, srcshadow + src_off, 0);
  verify_contents(file_name, 2, dstshadow, dst, bytes);
  verify_contents(file_name, 3, srcshadow, src, bytes);
  ....
}


Advertencia de PVS-Studio: V575 [CWE-628] La función 'memmove' procesa elementos '0'. Examine el tercer argumento. memmove_common.c 82



La función mueve 0 bytes. ¿Qué es? ¿Prueba de unidad? ¿Error de tipografía?



Para mí, este código es incomprensible y extraño.



¿Por qué utilizar analizadores de código?



Puede parecer que, dado que se encontraron pocos errores, la introducción del analizador en el proceso de desarrollo de código no es razonable. Pero el objetivo de utilizar herramientas de análisis estático no está en las comprobaciones únicas, sino en la detección regular de errores en la etapa de escritura del código. De lo contrario, estos errores se detectan de formas más costosas y más lentas (depuración, pruebas, reseñas de usuarios, etc.). Esta idea se describe con más detalle en el artículo " Errores que el análisis de código estático no puede encontrar porque no se utiliza ", que recomiendo familiarizarse. Y luego visite nuestro sitio web para descargar y probar PVS-Studio para verificar sus proyectos.



¡Gracias por su atención!





Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace de traducción: Andrey Karpov. Análisis de código estático de la colección de la biblioteca PMDK por Intel y errores que no son errores reales .



All Articles