Comience a recopilar errores en las funciones de copia

memcpy

He notado varias veces que los programadores cometen errores en funciones simples de copia de datos. Este tema requerir谩 mucho m谩s tiempo en el futuro para estudiar y seleccionar material para escribir un art铆culo completo. Pero quer铆a compartir un par de ejemplos que not茅 recientemente.

驴El fen贸meno Baadera-Meinhof? No, no lo creo


Como miembro del equipo PVS-Studio, encuentro una gran cantidad de errores que descubrimos en varios proyectos. Me gusta DevRel: me gusta hablar de eso :). Hoy decid铆 hablar sobre las funciones de copia de datos implementadas incorrectamente.

Me he encontrado con tales funciones fallidas m谩s de una vez. Pero no los escrib铆, porque no le di ninguna importancia a esto. Sin embargo, desde que not茅 esta tendencia, es hora de comenzar a recopilarlos. Para comenzar, compartir茅 los dos 煤ltimos casos notados.

Alguien puede argumentar que hay dos casos: esto no es una regularidad. Y eso, tal vez, llam茅 la atenci贸n sobre ellos 煤nicamente porque me conocieron despu茅s de un corto per铆odo de tiempo y desencadenaron el fen贸meno Baader-Meinhof .

El fen贸meno Baader-Meinhof, tambi茅n la ilusi贸n de frecuencia, es una distorsi贸n cognitiva en la que la informaci贸n recientemente descubierta que aparece nuevamente despu茅s de un corto per铆odo de tiempo se percibe como inusualmente frecuente.

Creo que esto no es as铆. Ya ten铆a la experiencia de tal observaci贸n sobre las funciones de comparaci贸n, que luego fue confirmada por el material recopilado: "El mal vive en las funciones de comparaci贸n ".

Bien, vamos al grano. La introducci贸n para dar solo dos ejemplos hasta ahora ha sido demasiado larga :).

Ejemplo N1


En el art铆culo sobre la verificaci贸n Zephyr RTOS, describ铆 un intento fallido de implementar la funci贸n strdup anal贸gica :

static char *mntpt_prepare(char *mntpt)
{
  char *cpy_mntpt;

  cpy_mntpt = k_malloc(strlen(mntpt) + 1);
  if (cpy_mntpt) {
    ((u8_t *)mntpt)[strlen(mntpt)] = '\0';
    memcpy(cpy_mntpt, mntpt, strlen(mntpt));
  }
  return cpy_mntpt;
}

Advertencia de PVS-Studio: V575 [CWE-628] La funci贸n 'memcpy' no copia toda la cadena. Use la funci贸n 'strcpy / strcpy_s' para preservar la terminal nula. shell.c 427

El analizador informa que la funci贸n memcpy copia la l铆nea, pero no copia el terminal cero, y esto es muy sospechoso. Parece que este terminal 0 se copia aqu铆:

((u8_t *)mntpt)[strlen(mntpt)] = '\0';

No, hay un error tipogr谩fico aqu铆, debido a que el terminal cero se copia a s铆 mismo. Tenga en cuenta que escribir en la matriz mntpt , no cpy_mntpt . Como resultado, la funci贸n mntpt_prepare devuelve una cadena que est谩 incompleta con un terminal cero.

De hecho, el programador quer铆a escribir as铆:

((u8_t *)cpy_mntpt)[strlen(mntpt)] = '\0';

No est谩 claro por qu茅 el c贸digo est谩 escrito de manera tan confusa y no est谩ndar. Como resultado, se cometi贸 un grave error en una funci贸n peque帽a y sin complicaciones. Este c贸digo se puede simplificar a la siguiente opci贸n:

static char *mntpt_prepare(char *mntpt)
{
  char *cpy_mntpt;

  cpy_mntpt = k_malloc(strlen(mntpt) + 1);
  if (cpy_mntpt) {
    strcpy(cpy_mntpt, mntpt);
  }
  return cpy_mntpt;
}

Ejemplo N2


void myMemCpy(void *dest, void *src, size_t n) 
{ 
   char *csrc = (char *)src; 
   char *cdest = (char *)dest; 
   for (int i=0; i<n; i++) 
     cdest[i] = csrc[i]; 
}

No identificamos este c贸digo nosotros mismos usando PVS-Studio, pero accidentalmente lo encontr茅 en el sitio web de StackOverflow: C y an谩lisis de c贸digo est谩tico: 驴Es esto m谩s seguro que memcpy?

Sin embargo, si marca esta funci贸n utilizando el analizador PVS-Studio, notar谩 correctamente:

  • V104 Conversi贸n impl铆cita de 'i' a tipo memsize en una expresi贸n aritm茅tica: i <n test.cpp 26
  • V108 Tipo de 铆ndice incorrecto: cdest [no es de tipo memsize]. Utilice el tipo memsize en su lugar. test.cpp 27
  • V108 Tipo de 铆ndice incorrecto: csrc [no es de tipo memsize]. Utilice el tipo memsize en su lugar. test.cpp 27

De hecho, este c贸digo contiene una falla, como se indica en las respuestas a StackOverflow. No puede usar una variable int como 铆ndice . En un programa de 64 bits, casi con certeza (no consideramos arquitecturas ex贸ticas), la variable int ser谩 de 32 bits y la funci贸n no podr谩 copiar m谩s de INT_MAX bytes. Aquellos. No m谩s de 2 gigabytes.

Con un b煤fer m谩s grande para copiar, se producir谩 un desbordamiento de la variable de signo, que desde el punto de vista de C y C ++ es un comportamiento indefinido. Y, por cierto, no intente adivinar exactamente c贸mo se manifestar谩 el error. Este es en realidad un tema dif铆cil, sobre el que puede leer en el art铆culo "El comportamiento indefinido est谩 m谩s cerca de lo que piensa ".

Es especialmente divertido que este c贸digo apareciera como un intento de eliminar alguna advertencia del analizador Checkmarx que se produjo cuando se llam贸 a la funci贸n memcpy . El programador no encontr贸 nada mejor que hacer su propia bicicleta. Y a pesar de la simplicidad de la funci贸n de copia, a煤n result贸 ser incorrecta. Es decir, de hecho, la persona probablemente lo hizo a煤n peor de lo que era. En lugar de comprender el motivo de la advertencia, enmascar贸 el problema escribiendo su propia funci贸n (confundiendo el analizador). Adem谩s agreg贸 un error usando int para el contador . Ah, s铆, ese c贸digo a煤n puede dificultar la optimizaci贸n. Es ineficiente usar su propio c贸digo en lugar de la eficiente funci贸n de memoria optimizada . No hagas esto :)

Conclusi贸n


Bueno, solo estoy al comienzo del viaje y, probablemente, tomar谩 m谩s de un a帽o antes de que acumule materiales para una publicaci贸n exhaustiva sobre este tema. En realidad, solo ahora comenzar茅 a escribir tales casos. Gracias por su atenci贸n y mire qu茅 interesante encontrar谩 el analizador PVS-Studio en su c贸digo C / C ++ / C # / Java.



Si desea compartir este art铆culo con una audiencia de habla inglesa, utilice el enlace a la traducci贸n: Andrey Karpov. Inicio de mi colecci贸n de errores encontrados en las funciones de copia .

All Articles