¿Es posible ejecutar código en el espacio de almacenamiento dinámico?

8

¿Me gustaría saber si puedo ejecutar una pieza de código dentro del espacio de almacenamiento dinámico?

Sen
fuente
1
Si su pregunta es sobre cómo bifurcarse a un fragmento de código en un lenguaje de programación en particular, pregunte en Desbordamiento de pila .
Gilles 'SO- deja de ser malvado'

Respuestas:

5

Tal vez. Si el montón es ejecutable, puede bifurcarse a ese código. Pero algunas variantes de Unix hacen que el espacio de almacenamiento dinámico no sea ejecutable , a fin de hacer que las vulnerabilidades de algunas vulnerabilidades de seguridad, como los desbordamientos de búfer, sean más difíciles (incluso si puede inyectar código en un programa, es posible que no pueda bifurcarse). (Consulte el artículo vinculado para una discusión de las variantes de Unix y su configuración). También algunas arquitecturas de procesador tienen cachés separadas para código y datos , por lo que es posible que deba emitir una instrucción de descarga de caché. Con todo, esto no es algo que quieras hacer a mano.

Existe una API estándar de Unix para cargar y ejecutar código, que hará lo necesario para que el código cargado sea ejecutable: dlopen . El código debe cargarse desde un archivo.

Los compiladores just-in-time suelen intentar encontrar interfaces más rápidas que dlopen. Tienen que gestionar las formas altamente dependientes de la plataforma para garantizar la ejecución del código.

EDITAR: Gracias a Bruce Ediger por recordarme la necesidad de vaciar el caché.

Gilles 'SO- deja de ser malvado'
fuente
4

En algunos equipos (como las CPU HP-PA de HP) es mucho más difícil, y en otros (como la CPU DEC Alpha) primero debe hacer un vaciado de caché de instrucciones, pero sí, en general, puede ejecutar código en el montón. El siguiente es un programa de lenguaje C razonablemente decente que ejecuta código "en el montón".

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

/* $Id: cpup4.c,v 1.2 1999/02/25 05:12:53 bediger Exp bediger $ */

typedef int (*fxptr)(
                int, int, int (*)(const char *, ...),
                void *,
                void *(*)(void *, const void *, size_t),
                void *(*)(size_t),
                void (*)(void *),
                size_t
);

char *signal_string(int sig);
void  signal_handler(int sig);
int   main(int ac, char **av);
int copyup(
                int i,
                int j,
                int (*xptr)(const char *, ...),
                void *yptr,
                void *(*bptr)(void *, const void *, size_t),
                void *(*mptr)(size_t),
                void (*ffptr)(void *),
                size_t  size
);
void f2(void);

/* return a string for the most common signals this program
 * will generate.  Probably could replace this with strerror()
 */
char *
signal_string(sig)
                int sig;
{
                char *bpr = "Don't know what signal";

                switch (sig)
                {
                case SIGILL:
                                bpr = "Illegal instruction";
                                break;
                case SIGSEGV:
                                bpr = "Segmentation violation";
                                break;
                case SIGBUS:
                                bpr = "Bus error";
                                break;
                }

                return bpr;
}

/* Use of fprintf() seems sketchy.  I think that signal_handler() doesn't
 * need special compiler treatment like generating Position Independent
 * Code.  It stays in one place, and the kernel knows that place.
 */
void
signal_handler(int sig)
{
                (void)fprintf(
                                stderr,
                                "%s: sig = 0x%x\n",
                                signal_string(sig),
                                sig
                );

                exit(99);
}

int
main(int ac, char **av)
{
                int i, j;

                /* check to see if cmd line has a number on it */
                if (ac < 2) {
                                printf("not enough arguments\n");
                                exit(99);
                }
                /* install 3 different signal handlers - avoid core dumps */
                if (-1 == (i = (long)signal(SIGSEGV, signal_handler)))
                {
                                perror("Installing SIGSEGV signal failed");
                                exit(33);
                }
                if (-1 == (i = (long)signal(SIGILL, signal_handler)))
                {
                                perror("Installing SIGILL signal handler failed");
                                exit(33);
                }
                if (-1 == (i = (long)signal(SIGBUS, signal_handler)))
                {
                                perror("Installing SIGBUS signal handler failed");
                                exit(33);
                }

                setbuf(stdout, NULL);

                /*
                 * print out addresses of original functions so there is something to
                 * reference during recursive function copying and calling 
                 */
                printf(
           "main = %p, copyup %p, memcpy %p, malloc %p, printf %p, free %p, size %ld\n",
           main, copyup, memcpy, malloc, printf, free, (size_t)f2 - (size_t)copyup);

                if ((i = atoi(*(av + 1))) < 1) {
                                printf(" i = %d, i must be > 1\n", i);
                                exit(99);
                }

                printf(" going for %d recursions\n", i);

                j = copyup(1, i, printf, copyup, memcpy, malloc, free, (size_t)f2 - (size_t)copyup);

                printf("copyup at %p returned %d\n", copyup, j);


                return 1;
}

int
copyup(
                int i, int j,
                int   (*xptr)(const char *, ...),
                void *yptr,
                void *(*bptr)(void *, const void*, size_t),
                void *(*mptr)(size_t),
                void  (*ffptr)(void *),
                size_t   size
)
{
                fxptr fptr;
                int k;

                if (i == j)
                {
                                (*xptr)("function at %p got to %d'th copy\n", yptr, i);
                                return i;
                } else
                                (*xptr)("function at %p, i = %d, j = %d\n", yptr, i, j);

                if (!(fptr = (fxptr)(*mptr)(size)))
                {
                                (*xptr)("ran out of memory allocating new function\n");
                                return -1;
                }

                (*bptr)(fptr, yptr, size);

                k = (*fptr)(i + 1, j, xptr, (void *)fptr, bptr, mptr, ffptr, size);

                (*xptr)("function at %p got %d back from function at %p\n",
                                yptr, k, fptr);

                (*ffptr)(fptr);

                return (k + 1);
}

void f2(void) {return;}
Bruce Ediger
fuente
1
He intentado esto, pero no entiendo por qué me sale un error de segmentación para todos los valores que no sean 1.
Sen
Un argumento de 1 simplemente ejecuta la función, aunque la función creará una copia de sí misma en el montón, no ejecuta la copia del montón. No sé por qué obtienes una violación de segmentación. ¿Puede dar más detalles sobre el compilador, la CPU, el sistema operativo, etc., etc.?
Bruce Ediger