{"id":2091,"date":"2018-08-26T10:14:20","date_gmt":"2018-08-26T10:14:20","guid":{"rendered":"http:\/\/blog.rastersoft.com\/?p=2091"},"modified":"2018-08-26T10:14:20","modified_gmt":"2018-08-26T10:14:20","slug":"presentando-crust","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=2091","title":{"rendered":"Presentando CRUST"},"content":{"rendered":"<p>Acabo de lanzar CRUST. Se trata de un analizador est\u00e1tico de C que permite disponer en C de una gesti\u00f3n de memoria similar a la de RUST.<\/p>\n<p>Y es que RUST est\u00e1 de moda, pues al ofrecer seguridad en el acceso a memoria din\u00e1mica pero sin necesidad de un runtime (como un recolector de basura) o de otras t\u00e9cnicas (como el conteo de referencias), permite exprimir al m\u00e1ximo el rendimiento. El problema es que RUST es un lenguaje nuevo, con su sintaxis propia (que, adem\u00e1s, diverge de la de C u otros lenguajes bastante), y que, por tanto, tiene una curva de aprendizaje.<\/p>\n<p>Por otro lado, existen casos en los que no se puede utilizar (todav\u00eda) RUST, como el de un microcontrolador PIC, Atmel&#8230;, pues hace falta un compilador espec\u00edfico. En otros microcontroladores, como los basados en ARM, <a href=\"http:\/\/blog.japaric.io\/quickstart\/\">es posible utilizarlo<\/a>, pero sigue teniendo el problema de que no es un compilador oficial, y por tanto hay que hacer alg\u00fan que otro malabar para integrarlo en la <em>toolchain<\/em> del fabricante.<\/p>\n<p>Es aqu\u00ed donde CRUST hace su aparici\u00f3n: como ya dije se trata de un analizador est\u00e1tico de C que permite disponer de (m\u00e1s o menos, claro) las mismas comprobaciones de seguridad que ofrece RUST para la gesti\u00f3n de memoria din\u00e1mica, de manera que es m\u00e1s dif\u00edcil que un programa sufra <a href=\"https:\/\/en.wikipedia.org\/wiki\/Dangling_pointer\">referencias colgantes o dangling pointers<\/a>, o <a href=\"https:\/\/en.wikipedia.org\/wiki\/Memory_leak\">p\u00e9rdidas de memoria<\/a>.<\/p>\n<p>A la hora de dise\u00f1ar CRUST ten\u00eda una cosa muy clara en mente: no pod\u00eda crear un nuevo lenguaje parecido a C, sino que ten\u00eda que seguir siendo C puro, compilable con absolutamente cualquier compilador est\u00e1ndar. Eso eliminaba cualquier tipo de preprocesador del estilo de <a href=\"https:\/\/en.wikipedia.org\/wiki\/Meta-object_System\">Metaobject<\/a> o similares. Tambi\u00e9n supon\u00eda rechazar cualquier tipo de conjunto de macros que pudiese alterar el c\u00f3digo de la m\u00e1s m\u00ednima manera. Y por supuesto, el uso de bibliotecas estaba completamente descartado.<\/p>\n<p>La soluci\u00f3n consisti\u00f3 en crear una serie de calificadores espec\u00edficos, similares en funcionamiento a los calificadores <em>volatile<\/em> o <em>const<\/em> ya disponibles en C, que permitan al analizador saber si un puntero concreto es <em>gestionado<\/em> o <em>no-gestionado<\/em>, as\u00ed como otras propiedades importantes para el analizador. Estos calificadores comienzan todos con el prefijo <em>__crust_ <\/em>para evitar interferencias con nombres de variables o futuras adiciones al lenguaje C. La clave de estos calificadores es que no son necesarios en absoluto para compilar el c\u00f3digo.<\/p>\n<p>Por supuesto, ning\u00fan compilador aceptar\u00eda un c\u00f3digo con dichos calificadores, y por eso es necesario incluir un fichero de cabecera (que se incluye con el analizador est\u00e1tico) que define dichos nuevos calificadores como espacios en blanco para el preprocesador de C. De esta manera, a la hora de compilar estos calificadores simplemente \u00abdesaparecen\u00bb, y s\u00f3lo son tenidos en cuenta cuando se utiliza el analizador. Este es un trozo de dicho fichero de cabecera, para que se entienda mejor:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">#ifndef ENABLE_CRUST_TAGS\r\n\r\n#ifndef __crust__\r\n#define __crust__\r\n#endif\r\n\r\n#ifndef __crust_borrow__\r\n#define __crust_borrow__\r\n#endif\r\n\r\n#ifndef __crust_recycle__\r\n#define __crust_recycle__\r\n#endif\r\n\r\n#ifndef __crust_alias__\r\n#define __crust_alias__\r\n#endif\r\n\r\n#ifndef __crust_no_0__\r\n#define __crust_no_0__\r\n#endif\r\n\r\n...\r\n<\/pre>\n<\/div>\n<p>Como se ve, se define cada posible calificador como una cadena vac\u00eda, lo que hace que el preprocesador se encargue de limpiar el c\u00f3digo y dejarlo listo para el compilador, sin necesidad de modificar nada. Esto permite programar como de costumbre, simplemente etiquetando aquellos punteros que deben ser gestionados como un bloque CRUST, y compilando el c\u00f3digo normalmente con el <em>toolchain<\/em> habitual, y s\u00f3lo de vez en cuando pasar el analizador est\u00e1tico para comprobar si hemos cometido alg\u00fan error al liberar o utilizar uno de estos bloques. Por supuesto, no es necesario escribir este fichero a mano, sino que se puede generar autom\u00e1ticamente simplemente llamando al analizador est\u00e1tico con el comando <em>crust &#8211;headers<\/em>, con lo que generar\u00e1 dicho fichero en el directorio actual.<\/p>\n<p>La base de las reglas de gesti\u00f3n de memoria de CRUST (y, por extensi\u00f3n, de RUST) es que cada funci\u00f3n es responsable de todos los bloques de memoria que genera o recibe. As\u00ed, si una funci\u00f3n pide un bloque de memoria din\u00e1mica (por ejemplo con malloc), es su responsabilidad liberarlo o asegurarse que sea liberado. Esto puede ocurrir de tres maneras diferentes:<\/p>\n<ul>\n<li>Puede liberar el bloque directamente ella misma<\/li>\n<li>Puede llamar a otra funci\u00f3n pasando dicho bloque como un par\u00e1metro, de manera que pase a ser responsabilidad de la nueva funci\u00f3n garantizar que se libere dicho bloque<\/li>\n<li>Puede devolver el bloque a la funci\u00f3n llamante, de manera que \u00e9sta recibe la responsabilidad de liberarlo<\/li>\n<\/ul>\n<p>No hay mucho m\u00e1s. Por supuesto existen, a mayores, otros detalles que hacen que la cosa no sea tan sencilla, por lo que para una explicaci\u00f3n m\u00e1s en profundidad recomiendo leer <a href=\"https:\/\/doc.rust-lang.org\/book\/second-edition\/ch04-01-what-is-ownership.html\">como es el modelo de memoria de RUST<\/a>.<\/p>\n<p>Un ejemplo sencillo de como trabaja CRUST se puede ver en este trozo de c\u00f3digo:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">\/\/ SIEMPRE a\u00f1adimos crust.h al principio\r\n\/\/ El fichero tiene que estar en el proyecto\r\n#include \"crust.h\"\r\n#include &lt;unistd.h&gt;\r\n\r\n\/\/ Definimos una estructura como \"gestionada\"\r\n\/\/ simplemente a\u00f1adiendo __crust__ a su definici\u00f3n\r\n\/\/ Utilizamos un typedef para ahorrarnos tener que poner\r\n\/\/ __crust__ en todos los sitios donde se utiliza\r\ntypedef __crust__ struct {\r\n\tint member;\r\n\tint p1;\r\n\tint p2;\r\n} *un_tipo_t;\r\n\r\n\/\/ esta funci\u00f3n crea un nuevo bloque \"gestionado\" y lo devuelve\r\n\r\nun_tipo_t funcion1();\r\n\r\n\/\/ esta funci\u00f3n recibe un bloque \"gestionado\",\r\n\/\/ pero no lo libera antes de salir\r\n\r\nvoid funcion2(un_tipo_t __crust_borrow__ parametro);\r\n\r\n\/\/ esta funci\u00f3n recibe un puntero a un bloque \"gestionado\",\r\n\/\/ y adem\u00e1s lo libera antes de salir\r\n\r\nuint32_t funcion3(un_tipo_t parametro);\r\n\r\nvoid main() {\r\n\r\n\tun_tipo_t bloque = funcion1();\r\n\r\n\tfuncion2(bloque);\r\n\tfuncion3(bloque);\r\n}\r\n<\/pre>\n<\/div>\n<p>Aqu\u00ed vemos varias cosas:<\/p>\n<ul>\n<li>Primero hacemos un typedef de un puntero a una estructura, y adem\u00e1s incluimos el calificador <em>__crust__<\/em>. Esto significa que absolutamente cualquier variable de tipo <em>un_tipo_t<\/em> ser\u00e1 <em>gestionada<\/em>, y por tanto sujeta a las reglas de CRUST.<\/li>\n<li>Luego tenemos tres definiciones de funciones que \u00abhacen cosas\u00bb con tipos <em>un_tipo_t<\/em>.<\/li>\n<li>Finalmente, tenemos el bloque <em>main<\/em>. En \u00e9l creamos un puntero de tipo <em>un_tipo_t<\/em> y le asignamos el bloque que nos devuelve <em>funcion2<\/em>.<\/li>\n<li>A continuaci\u00f3n llamamos con dicho bloque a <em>funcion3<\/em>. Como dicho par\u00e1metro est\u00e1 marcado como <em>__crust_borrow__<\/em>, sabemos que dicha funci\u00f3n nunca liberar\u00e1 dicho bloque, por lo que despu\u00e9s de llamarla seguir\u00e1 estando disponible y podemos seguir utiliz\u00e1ndolo.<\/li>\n<li>Finalmente llamamos tambi\u00e9n con dicho bloque a funcion1. Como el par\u00e1metro de dicha funci\u00f3n no est\u00e1 marcado como __crust_borrow__, sabemos a ciencia cierta que ese bloque que estamos pasando va a ser liberado dentro de ella, por lo que a partir de este punto no podemos volver a utilizarlo.<\/li>\n<li>Llegamos al final de la funci\u00f3n, y como la variable <em>bloque <\/em>ya no apunta a nada (pues el bloque fue liberado al llamar a <em>funcion3<\/em>), no hay riesgo de que tengamos una fuga de memoria.<\/li>\n<\/ul>\n<p>Este c\u00f3digo no devolver\u00eda ning\u00fan error al pasar por el analizador est\u00e1tico CRUST precisamente porque cumple con precisi\u00f3n las reglas de gesti\u00f3n de memoria. Sin embargo, si hici\u00e9semos un cambio tan sencillo como invertir el orden de las llamadas a <em>funcion2()<\/em> y <em>funcion3()<\/em>, obtendr\u00edamos un error:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">ERROR: Argument 1 when calling function 'funcion2' at line 41 was freed at line 40\r\nTotal: 1 errors.\r\n<\/pre>\n<\/div>\n<p>El motivo es que <em>funcion2()<\/em> libera el bloque de memoria que recibe, lo que significa que cuando llamamos despu\u00e9s a <em>funcion3()<\/em> con \u00e9l, CRUST sabe que ese bloque de memoria ya no existe, y nos avisa.<\/p>\n<p>Algo similar ocurre si s\u00f3lo llamamos a <em>funcion2()<\/em> (que sabemos que no libera el bloque) pero no llamamos a <em>funcion3()<\/em>:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">ERROR: Memory block 'bloque', initialized at line 38, is still in use at exit point in line 41\r\nTotal: 1 errors.<\/pre>\n<\/div>\n<p>Aqu\u00ed CRUST se da cuenta de que el bloque que hemos inicializado no ha sido liberado al llegar al final de la funci\u00f3n. Si lo dej\u00e1semos as\u00ed tendr\u00edamos una fuga de memoria, y por eso nos avisa diligentemente.<\/p>\n<p>Por supuesto CRUST es lo suficientemente inteligente como para seguir las posibles ramas de ejecuci\u00f3n del c\u00f3digo. Probemos a modificar la funci\u00f3n <em>main()<\/em> anterior y dej\u00e9mosla as\u00ed:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">void main() {\r\n\t\/\/ \"tmp\" tiene un valor que desconocemos\r\n\tuint8_t tmp;\r\n\r\n\tun_tipo_t bloque = funcion1();\r\n\r\n\tif (tmp == 5) {\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (tmp == 8) {\r\n\t\tbloque = NULL;\r\n\t}\r\n\r\n\tif (tmp == 7) {\r\n\t\tbloque = funcion1();\r\n\t}\r\n\r\n\tif (tmp != 3) {\r\n\t\tfuncion3(bloque);\r\n\t}\r\n\tfuncion2(bloque);\r\n}\r\n<\/pre>\n<\/div>\n<p>Al pasar este c\u00f3digo a trav\u00e9s de CRUST obtenemos el siguiente resultado:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">ERROR: Memory block 'bloque', initialized at line 42, is still in use at exit point in line 45\r\nERROR: Assignment to 'bloque' at line 49, which was already assigned at line 42\r\nERROR: Argument 1 when calling function 'funcion2' at line 59 was freed at line 57\r\nERROR: Memory block 'bloque', initialized at line 53, is still in use at exit point in line 60\r\nERROR: Assignment to 'bloque' at line 53, which was already assigned at line 42\r\nERROR: Memory block 'bloque', initialized at line 42, is still in use at exit point in line 60\r\nTotal: 6 errors.<\/pre>\n<\/div>\n<p>Aqu\u00ed nos est\u00e1 avisando de todos los errores que hemos cometido, que son:<\/p>\n<ul>\n<li>Si <em>tmp<\/em> vale 5 saldremos en el <em>return<\/em> de la primera comparaci\u00f3n, con lo que el bloque que inicializamos en la l\u00ednea 42 no se libera y tendremos una fuga de memoria.<\/li>\n<li>Si <em>tmp<\/em> vale 7 u 8 estaremos sobreescribiendo un puntero que apunta a un bloque v\u00e1lido en la l\u00ednea 49, con lo que tendremos una fuga de memoria.<\/li>\n<li>Si <em>tmp<\/em> tiene un valor diferente de 3 liberaremos el bloque en la l\u00ednea 49, con lo que al llamar a <em>funcion2()<\/em> tendremos una referencia colgante.<\/li>\n<li>Si <em>tmp<\/em> vale 3 todo parecer\u00e1 funcionar correctamente hasta llegar al final de la funci\u00f3n, donde nos encontraremos con que el bloque nunca se libera y tendremos una fuga de memoria. Este error nos aparece dos veces porque en una de las ramas de ejecuci\u00f3n no liberamos el bloque recibido al principio (l\u00ednea 42) y en la otra no liberamos el bloque obtenido cuando <em>tmp<\/em> vale 7.<\/li>\n<\/ul>\n<p>Por supuesto, CRUST tiene algunas limitaciones. Por ejemplo, s\u00f3lo recuerda si una variable es NULL (vale 0) o no (valor distinto de 0), pero no valores concretos. Esto significa que este c\u00f3digo ser\u00e1 analizado correctamente:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">void main() {\r\n\r\n\tun_tipo_t bloque = funcion1();\r\n\r\n\tif (bloque != NULL) {\r\n\t\tfuncion3(bloque);\r\n\t\tbloque = NULL;\r\n\t}\r\n\r\n\tif (bloque != NULL) {\r\n\t\tfuncion2(bloque);\r\n\t}\r\n}<\/pre>\n<\/div>\n<p>CRUST sabe que <em>bloque<\/em>, tal cual es devuelto por <em>funcion1()<\/em> puede ser NULL o no NULL, pero cuando llega al primer <em>if<\/em> y analiza ambas posibles ramas, en la de dentro del <em>if<\/em> marca a <em>bloque<\/em> como no NULL, y en la de fuera como NULL. Cuando llama a <em>funcion3()<\/em> el bloque es liberado, y por eso no devuelve un error al asignar NULL a dicha variable. A partir de aqu\u00ed ambas ramas de ejecuci\u00f3n tienen NULL como valor de <em>bloque<\/em>, y CRUST es capaz de detectar correctamente que jam\u00e1s se llamar\u00e1 a <em>funcion2()<\/em>, y por eso no devuelve ning\u00fan error.<\/p>\n<p>Sin embargo, este bloque s\u00ed dar\u00eda errores, pues CRUST no llega a tener un nivel de control tan fino de los valores de las variables:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">void main() {\r\n\r\n\tuint8_t tmp;\r\n\r\n\tun_tipo_t bloque = funcion1();\r\n\r\n\tif (tmp == 3) {\r\n\t\tfuncion3(bloque);\r\n\t}\r\n\r\n\tif (tmp != 3) {\r\n\t\tfuncion2(bloque);\r\n\t\tfuncion3(bloque);\r\n\t}\r\n}\r\n<\/pre>\n<\/div>\n<p>Este c\u00f3digo devolver\u00eda estos errores:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">ERROR: Argument 1 when calling function 'funcion2' at line 48 was freed at line 44\r\nERROR: Argument 1 when calling function 'funcion3' at line 49 was freed at line 44\r\nERROR: Memory block 'bloque', initialized at line 41, is still in use at exit point in line 51\r\nTotal: 3 errors.\r\n<\/pre>\n<\/div>\n<p>Por supuesto, la forma correcta de hacer lo anterior ser\u00eda esta:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">void main() {\r\n\r\n\tuint8_t tmp;\r\n\r\n\tun_tipo_t bloque = funcion1();\r\n\r\n\tif (tmp == 3) {\r\n\t\tfuncion3(bloque);\r\n\t} else {\r\n\t\tfuncion2(bloque);\r\n\t\tfuncion3(bloque);\r\n\t}\r\n}\r\n<\/pre>\n<\/div>\n<p>La cual s\u00ed ser\u00eda analizada correctamente por CRUST.<\/p>\n<p>Todo esto no son m\u00e1s que unas pinceladas, pues hay mucho m\u00e1s en CRUST (por ejemplo el <em>prestamo<\/em> de bloques, igual que en RUST), por lo que lo mejor es leerse la documentaci\u00f3n completa, que viene en formato PDF.<\/p>\n<p>Como de costumbre, se puede <a href=\"http:\/\/www.rastersoft.com\/programas\/crust.html\">encontrar en mi p\u00e1gina web<\/a> y en el <a href=\"https:\/\/gitlab.com\/rastersoft\/crust\">respositorio de CRUST en GitLab<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Acabo de lanzar CRUST. Se trata de un analizador est\u00e1tico de C que permite disponer en C de una gesti\u00f3n de memoria similar a la de RUST. Y es que RUST est\u00e1 de moda, pues al ofrecer seguridad en el acceso a memoria din\u00e1mica pero sin necesidad de un runtime (como un recolector de basura) &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=2091\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">Presentando CRUST<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,7],"tags":[],"class_list":["post-2091","post","type-post","status-publish","format-standard","hentry","category-nueva-version","category-tutoriales"],"_links":{"self":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2091","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2091"}],"version-history":[{"count":27,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2091\/revisions"}],"predecessor-version":[{"id":2118,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2091\/revisions\/2118"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2091"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2091"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2091"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}