stringtranslate.com

Puntero nulo

En informática , un puntero nulo o una referencia nula es un valor que se guarda para indicar que el puntero o la referencia no se refieren a un objeto válido . Los programas utilizan habitualmente punteros nulos para representar condiciones como el final de una lista de longitud desconocida o la imposibilidad de realizar alguna acción; este uso de punteros nulos se puede comparar con los tipos que aceptan valores nulos y con el valor Nothing en un tipo de opción .

Un puntero nulo no debe confundirse con un puntero no inicializado : se garantiza que un puntero nulo se comparará de manera desigual con cualquier puntero que apunte a un objeto válido. Sin embargo, en general, la mayoría de los lenguajes no ofrecen dicha garantía para punteros no inicializados. Puede compararse igual a otros punteros válidos; o puede compararse igual a punteros nulos. Puede hacer ambas cosas en diferentes momentos; o la comparación puede ser un comportamiento indefinido . Además, en lenguajes que ofrecen dicho soporte, el uso correcto depende de la experiencia individual de cada desarrollador y de las herramientas de linter. Incluso cuando se usan correctamente, los punteros nulos son semánticamente incompletos , ya que no ofrecen la posibilidad de expresar la diferencia entre un valor "No aplicable" frente a un valor "No conocido" o frente a un valor "Futuro".

Debido a que un puntero nulo no apunta a un objeto significativo, un intento de acceder a los datos almacenados en esa ubicación de memoria (no válida) puede provocar un error en tiempo de ejecución o un bloqueo inmediato del programa. Este es el error de puntero nulo . Es uno de los tipos más comunes de debilidades de software, [1] y Tony Hoare , quien introdujo el concepto, se ha referido a él como un "error de mil millones de dólares". [2]

do

En C , se garantiza que dos punteros nulos de cualquier tipo se comparen de manera igual. [3] La macro del preprocesador NULLse define como una constante de puntero nulo definida por la implementación en , [4] que en C99 se puede expresar de forma portable como , el valor entero convertido al tipo (véase puntero a tipo vacío ). [5] El estándar C no dice que el puntero nulo sea el mismo que el puntero a la dirección de memoria  0, aunque ese puede ser el caso en la práctica. Desreferenciar un puntero nulo es un comportamiento indefinido en C, [6] y se permite que una implementación conforme asuma que cualquier puntero que se desreferencia no es nulo.<stdlib.h>((void *)0)0 void*

En la práctica, la desreferenciación de un puntero nulo puede dar como resultado un intento de lectura o escritura desde una memoria que no está asignada, lo que desencadena un error de segmentación o una violación de acceso a la memoria. Esto puede manifestarse como un fallo del programa o transformarse en una excepción de software que puede ser detectada por el código del programa. Sin embargo, existen ciertas circunstancias en las que este no es el caso. Por ejemplo, en el modo real x86 , la dirección es legible y también normalmente escribible, y la desreferenciación de un puntero a esa dirección es una acción perfectamente válida pero normalmente no deseada que puede conducir a un comportamiento indefinido pero que no provoca fallos en la aplicación; si un puntero nulo se representa como un puntero a esa dirección, la desreferenciación conducirá a ese comportamiento. Hay ocasiones en las que la desreferenciación de un puntero a la dirección cero es intencional y está bien definida; por ejemplo, el código BIOS escrito en C para dispositivos x86 de modo real de 16 bits puede escribir la tabla de descriptores de interrupciones (IDT) en la dirección física 0 de la máquina al desreferenciar un puntero con el mismo valor que un puntero nulo para escritura. También es posible que el compilador optimice la desreferencia del puntero nulo, evitando un error de segmentación pero causando otro comportamiento no deseado. [7]0000:0000

C++

En C++, si bien la NULLmacro se heredó de C, tradicionalmente se ha preferido el literal entero para cero para representar una constante de puntero nulo. [8] Sin embargo, C++11 introdujo la constante de puntero nulo explícito nullptry el tipo nullptr_tque se utilizará en su lugar.

Otros idiomas

En algunos entornos de lenguaje de programación (al menos una implementación de Lisp propietaria, por ejemplo), [ cita requerida ] el valor utilizado como puntero nulo (llamado nilen Lisp ) puede ser en realidad un puntero a un bloque de datos internos útiles para la implementación (pero no explícitamente accesibles desde los programas de usuario), lo que permite utilizar el mismo registro como una constante útil y una forma rápida de acceder a los datos internos de la implementación. Esto se conoce como nilvector.

En lenguajes con una arquitectura etiquetada , un puntero posiblemente nulo puede reemplazarse con una unión etiquetada que impone un manejo explícito del caso excepcional; de hecho, un puntero posiblemente nulo puede verse como un puntero etiquetado con una etiqueta calculada.

Los lenguajes de programación utilizan diferentes literales para el puntero nulo . En Python, por ejemplo, un valor nulo se denomina None. En Pascal y Swift , un puntero nulo se denomina nil. En Eiffel , se denomina voidreferencia.

Desreferenciación nula

Debido a que un puntero nulo no apunta a un objeto significativo, un intento de desreferenciar (es decir, acceder a los datos almacenados en esa ubicación de memoria) un puntero nulo generalmente (pero no siempre) causa un error de tiempo de ejecución o un bloqueo inmediato del programa. MITRE enumera el error de puntero nulo como una de las debilidades de software más comúnmente explotadas. [9]

Mitigación

Si bien podríamos tener lenguajes sin valores nulos, la mayoría tiene la posibilidad de valores nulos, por lo que existen técnicas para evitar o ayudar a depurar desreferencias de punteros nulos. [12] Bond et al. [12] sugieren modificar la Máquina Virtual Java (JVM) para realizar un seguimiento de la propagación de valores nulos.

Tenemos tres niveles de manejo de referencias nulas, en orden de efectividad:

1. idiomas sin nulo;

2. lenguajes que pueden analizar estáticamente el código para evitar la posibilidad de una desreferencia nula en tiempo de ejecución;

3. Si puede ocurrir una desreferencia nula en tiempo de ejecución, herramientas que ayudan a la depuración.

Los lenguajes puramente funcionales son un ejemplo de nivel 1, ya que no se proporciona acceso directo a los punteros y todo el código y los datos son inmutables. El código de usuario que se ejecuta en lenguajes interpretados o de máquina virtual generalmente no sufre el problema de la desreferenciación de punteros nulos. [ dudosodiscutir ]

Cuando un lenguaje proporciona o utiliza punteros que podrían volverse nulos, es posible evitar desreferencias nulas en tiempo de ejecución proporcionando una verificación en tiempo de compilación a través de análisis estático u otras técnicas, con asistencia sintáctica de características del lenguaje como las que se ven en el lenguaje de programación Eiffel con seguridad Void [13] para evitar desreferencias nulas, D [ 14] y Rust [15] .

En algunos lenguajes se puede realizar un análisis utilizando herramientas externas, pero éstas son débiles en comparación con el soporte directo del lenguaje con verificaciones del compilador, ya que están limitadas por la definición del lenguaje en sí.

El último recurso del nivel 3 es cuando se produce una referencia nula en tiempo de ejecución; las ayudas de depuración pueden ayudar.

Alternativas a los punteros nulos

Como regla general, para cada tipo de estructura o clase, defina algunos objetos que representen algún estado de la lógica de negocios que reemplace el comportamiento indefinido en caso de un valor nulo. Por ejemplo, "future" para indicar un campo dentro de una estructura que no estará disponible en este momento (pero para el cual sabemos de antemano que en el futuro estará definido), "not apply" para indicar un campo en una estructura no normalizada, "error", "timeout" para indicar que el campo no pudo inicializarse (probablemente deteniendo la ejecución normal del programa, hilo, solicitud o comando completo).

Historia

En 2009, Tony Hoare afirmó [16] [17] que inventó la referencia nula en 1965 como parte del lenguaje ALGOL W. En esa referencia de 2009, Hoare describe su invención como un "error de mil millones de dólares":

Lo llamo mi error de mil millones de dólares. Fue la invención de la referencia nula en 1965. En ese momento, estaba diseñando el primer sistema de tipos integral para referencias en un lenguaje orientado a objetos (ALGOL W). Mi objetivo era asegurar que todo uso de referencias fuera absolutamente seguro, con comprobaciones realizadas automáticamente por el compilador. Pero no pude resistir la tentación de poner una referencia nula, simplemente porque era muy fácil de implementar. Esto ha llevado a innumerables errores, vulnerabilidades y fallas del sistema, que probablemente han causado mil millones de dólares de dolor y daños en los últimos cuarenta años.

Véase también

Notas

  1. ^ "CWE-476: Desreferencia de puntero NULL". MITRE .
  2. ^ "Null References: The Billion Dollar Mistake" (Referencias nulas: el error de mil millones de dólares). InfoQ . Consultado el 5 de septiembre de 2024 .
  3. ^ ISO/IEC 9899, ​​cláusula 6.3.2.3, párrafo 4.
  4. ^ ISO/IEC 9899, ​​cláusula 7.17, párrafo 3: NULL... que se expande a una constante de puntero nulo definida por la implementación...
  5. ^ ISO/IEC 9899, ​​cláusula 6.3.2.3, párrafo 3.
  6. ^ Véase ISO/IEC 9899, ​​cláusula 6.5.3.2, párrafo 4, especialmente nota al pie 87.
  7. ^ Lattner, Chris (13 de mayo de 2011). "Lo que todo programador de C debería saber sobre el comportamiento indefinido n.º 1/3". blog.llvm.org . Archivado desde el original el 14 de junio de 2023 . Consultado el 14 de junio de 2023 .
  8. ^ Stroustrup, Bjarne (marzo de 2001). "Capítulo 5: El calificador (§5.4) evita la redefinición accidental de y garantiza que se pueda utilizar cuando se requiere una constante". El lenguaje de programación C++ (14.ª edición de la 3.ª ed.). Estados Unidos y Canadá: Addison–Wesley. pág. 88. ISBN
    constNULLNULL 0-201-88954-4.
  9. ^ "CWE-476: Desreferencia de puntero NULL". MITRE .
  10. ^ El lenguaje de programación Objective-C 2.0 , sección "Envío de mensajes a nil".
  11. ^ "Desreferencia de puntero NULL del kernel explotable de OS X en AppleGraphicsDeviceControl"
  12. ^ ab Bond, Michael D.; Nethercote, Nicholas; Kent, Stephen W.; Guyer, Samuel Z.; McKinley, Kathryn S. (2007). "Rastreando manzanas podridas". Actas de la 22.ª conferencia anual ACM SIGPLAN sobre sistemas y aplicaciones de programación orientada a objetos - OOPSLA '07 . p. 405. doi :10.1145/1297027.1297057. ISBN 9781595937865. Número de identificación del sujeto  2832749.
  13. ^ "Seguridad en espacios vacíos: antecedentes, definición y herramientas" . Consultado el 24 de noviembre de 2021 .
  14. ^ Bartosz Milewski. «SafeD – Lenguaje de programación D» . Consultado el 17 de julio de 2014 .
  15. ^ "Seguridad sin miedo: seguridad de la memoria". Archivado desde el original el 8 de noviembre de 2020 . Consultado el 4 de noviembre de 2020 .
  16. ^ Tony Hoare (25 de agosto de 2009). "Referencias nulas: el error de mil millones de dólares". InfoQ.com.
  17. ^ Tony Hoare (25 de agosto de 2009). "Presentación: "Referencias nulas: el error de mil millones de dólares"". InfoQ.com.

Referencias