Git Product home page Git Product logo

kernel-fuzzer's Introduction

kernel-fuzzer

como funciona afl

vamos a instrumentar el ejercicio que hice para hackthebox para ver que parte se le agregan al binario y analizar que modificaciones le hace la instrumentacion de afl

compilamos nuestro challenge:

#include <iostream>
#include <fstream>
#include <string.h>
#include <unistd.h>

#define MAX_ANIMALS 10

using namespace std;

class Animal {
    public:
        string name;

        Animal() {
            cout << "Fabricando animalito " << endl;
        }
        
        virtual void talk(string) {};
};

class Cat : public Animal {
    public:
        void talk(string phrase) {
            cout << " _               "<< string(phrase.length(), '_') << endl;
            cout << "( \\             (" << phrase << ")" << endl;
            cout << " ) )             " << "-," << string(phrase.length()-2, '-') << endl;
            cout << "( (  .-''''-.  A.-.A" << endl;
            cout << " \\ \\/        \\/ , , \\" << endl;
            cout << "  \\   \\      =;  t  /=" << endl;
            cout << "   \\   |''''-  ',--'" << endl;
            cout << "    / //    | ||" << endl;
            cout << "   /_,))    |_,))" << endl;
        
        }
};

class Toad : public Animal {
    public:
        void talk(string phrase) {
            cout << "      " << string(phrase.length(), '_') << endl;
            cout << "     (" << phrase << ")" << endl;
            cout << "      -," << string(phrase.length()-2, '-') << endl;
            cout << "  @..@" << endl;
            cout << " (----)" << endl;
            cout << "( >__< )" << endl;
            cout << "^^ ~~ ^^" << endl;
        }
};

class Dog : public Animal {
    public:
        void talk(string phrase) {
            cout << "       " << string(phrase.length(), '_') << endl;
            cout << "      (" << phrase << ")" << endl;
            cout << "       -," << string(phrase.length()-2, '-') << endl;
            cout << "    ___" << endl;
            cout << "---'o  \\                         ,"<< endl;
            cout << "\\,__ | )                        ))" << endl;
            cout << "     \\;_/\\---------------------~//" << endl;
            cout << "     \\                           )" << endl;
            cout << "      (  .____________________. (\\" << endl;
            cout << "       ) ))                    ) ))" << endl;
            cout << "      '-''                    '_''" << endl;
        }
};

class ParserHistory {
    ifstream story;
    Animal *character[MAX_ANIMALS];
    unsigned int i=0;
    public:
        ParserHistory(char *fileName, bool verbose) {
            char data[100], *textLine, *repeatedLine;
            unsigned int tag;
            unsigned short characterIndex, lengthMessage, howMany;
            string animal;
            
            this->story.open(fileName);
            memset(data, 0, sizeof(data));
            
            this->story.read(data, 11);
            
            if (strncmp(data, "LINUXSTORY#", 11) == 0) {
                if (verbose) {
                    cout << "Header detected" << endl;
                }
                
                do {
                    tag = 0;
                    this->story.read((char *)&tag, 3);
                    
                    if ( this->i <= MAX_ANIMALS ) {
                        if ( tag == 0x544143  ) {
                            character[this->i] = new Cat();
                            if (verbose ) {
                                cout << "cat memory address: " << hex << character[this->i] << endl;
                            }
                        } else if ( tag == 0x474f44 ) {
                            character[this->i] = new Dog();
                            if (verbose) {
                                cout << "dog memory address: " << hex << character[this->i] << endl;
                            }
                        } else if ( tag == 0x414f54) {
                            character[this->i] = new Toad();
                            if (verbose) {
                                cout << "toad memory address: " << hex << character[this->i] << endl;
                            }
                        }
                        this->i++;
                    }
                } while( tag != 0x444e45 );
                
                do {
                    tag = 0;
                    characterIndex = 0;
                    //memset(textLine, 0, sizeof(textLine));
                    
                    this->story.read((char *)&tag, 1);
                    
                    if ( tag == 'T' ) {
                        this->story.read((char *)&characterIndex, 2);
                        
                        if ( characterIndex < this->i ) {
                            this->story.read((char *)&lengthMessage, 2);
                            
                            if (lengthMessage <= 1024) {
                                textLine = new char(lengthMessage);
                                if (verbose) {
                                    cout << "string memory address: " << static_cast<void *>(textLine) << endl;
                                }
                                
                                if (textLine != NULL) {
                                    this->story.read(textLine, lengthMessage);
                                    this->character[characterIndex]->talk(textLine);
                                }
                            }
                        }
                    } else if ( tag == 'F' ) {
                        this->story.read((char *)&characterIndex, 2);
                        if (verbose) {
                            cout << "tag freed" << endl;
                            cout << "memory deleted: " << hex << character[characterIndex] << endl;
                        }
                        
                        delete this->character[characterIndex];
                    } else if (tag == 'C') {
                        this->story.read((char *)&characterIndex, 2);
                        
                        if ( characterIndex < this->i ) {
                            this->story.read((char *)&howMany, 2);
                            this->story.read((char *)&lengthMessage, 2);
                            
                            repeatedLine = new char(lengthMessage);
                            this->story.read(repeatedLine, lengthMessage);
                            
                            for(unsigned int i=0; i <= howMany; i++) {
                                textLine = new char(lengthMessage);
                                if (verbose) {
                                    cout << "string memory address: " << static_cast<void *>(textLine) << endl;
                                }
                                
                                strncpy(textLine, repeatedLine, lengthMessage);
                                this->character[characterIndex]->talk(textLine);
                            }
                        }
                        
                    } else {
                        if (verbose) {
                            cout << "invalid tag: " << tag << endl;
                        }
                    }
                } while( tag != 0x00);
                
            }
        }
};

int main(int argc, char **argv) {
    int opt;
    bool verbose;
    char *fileName;
    ParserHistory *director;
    
    verbose = false;
    while((opt = getopt(argc, argv, "f:v")) != -1) {
        switch(opt) {
            case 'f':
                cout << "[*] History file: " << optarg << endl;
                fileName = optarg;
                break;
            case 'v':
                verbose = true;
                break;
            default:
                cout << "[-] Unrecogniced option: " << opt << endl;
                break;
        }
    }
    
    director = new ParserHistory(fileName, verbose);
    return 0;
}

para compilarlo basta con:

afl-g++ -o orange-afl orange-afl.cpp
afl-cc 2.52b by <[email protected]>
afl-as 2.52b by <[email protected]>
[+] Instrumented 302 locations (64-bit, non-hardened mode, ratio 100%).

fuzzeando

para probar como se fuzzea, se le puede pegar desde un sample mas o menos armado, al toque encuentra los crashes

abriendo con ghidra

lo abrimos y al toque vemos que el main tiene incrustados un monton de calls a zonas nombradas __afl_maybe_log

estos calls a la funcion esa, se agregan por todo el binario, como podemos ver en la siguiente imagen:

el arranque del injerto solo verifica si la variable global __afl_area_ptr ya fue definida (es decir si ya paso una vez), en caso de que no este initialized, procede a cargarla saltado a __afl_setup

vamos a mirarlo como graph para darnos una idea de que es:

claramente es una serie de if encadenaditos con algun bucle dentro, haciendole zoom al primer basic block, vemos que estamos parados ahi

los primeros 3 basic blocks verifican si:

  • __afl_area_ptr no esta inicializado
  • __afl_setup_failure no esta en true
  • __afl_global_area_ptr esta en NULL

ejecuta esta zona donde carga el estado de todos los registros (esto es para preservar el estado que tiene el proyecto en la zona donde se va a forkear)

ahora recien ahora hace espacio en el stack para alvergar algunas variables y llama a getenv("AFL_SHM_ENV"), se esta activa es como que le dice "NO INSTRUMENTES" y se va para el __afl_setup_abort

y solo para saber un poquito mas, nos vamos a ver que es la funcion __afl_setup_abort

pone en 1 (true?puede ser mas incrementado) __afl_setup_failure restaura los registros como cuando estaban en el main y salta a un violento __afl_return

la inversa del prologo raro (me parece que tiene algo que ver con los flags, si entro por ser mayor, por ser menor, altera los flags y cambian de valor)

si esta todo bien, y no pasa por esa zona oscura, aterriza por aca:

que es el algoritmo que explica el chabon en sus famosos tutoriales

  cur_location = <COMPILE_TIME_RANDOM>;
  shared_mem[cur_location ^ prev_location]++; 
  prev_location = cur_location >> 1;

en el chunk de assembler que pegue, esta hasta la asignacion final, fijense aca:

INC byte ptr [RDX+RCX*0x1]

RDX ahi tiene la base del unsigned int array __afl_area_ptr y RCX indexa como char (lo digo por el *0x1)

la zona de memoria compartida de afl tiene un size de 64k, es decir 65536 bytes, entonces probablemente cur_location y prev_location deben ser unsigned short int

cur_location es un numero random para identificar el basic bloc que se pasa por ECX como argumento del CALL:

en la anterior imagen se puede ver como se carga RCX con un valorcito para identificar la zona, en ese caso 0xd435

aca tenemos otro ejemplo:

aca podemos ver que en cada basic block que se va para adentro, anade el marcador ese

hay algunos que no le da importancia como por ejmplo los dos que no tienen marcador

pero si vamos un poco mas para adentro del parser, vemos que le metio a los demas

no sabemos a ciencia cierta como es que elige que basic blocks instrumenta y cuales no, pero no nos interesa para nuestro trabajo final

tambien podemos notar, que se pasan mas argumentos de los que creiamos pero sin embargo no le da mucho bola dentro de la funcoin, asi que evitaremos analizarlos

si tenemos AFL_SHM_ENV

si tenemos activa esa variable de entorno entonces transforma ese ascii que consulto a int con atoi y lo usa como shmid para definir el identificador de la memoria compartida

segundo y tercer argumento estan en NULL (por lso dos xor que hay), shmat tiene la siguiente definicion:

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

en ese caso, nuestra llamada es:

shmat(atoi(getenv("ALF_SHM_ENV")), NULL, 0);

el hecho de que shmaddr fuera NULL, the system choose a suitable (unused) page-aligned address to attach the segment. El flag 0 no se que caracteristica magica le aporta

todo esto se esta ejecutando en el caminito __afl_setup_first

luego de preguntar cual era el area que le asigno a la shared memory lo guarda en __afl_area_ptr y __afl_global_area_ptr

forkserver

envia un mensajito de 4 caracteres al canal de comunicacion que tiene abierto con el forkserver

no se como sabe que el file descriptor es 0xc7, no nos vamos a centrar en eso, pero en caso de que responda que no le llegaron bien los 4 caracteres, se va al resume

que solo cierra los descriptores fijos de comunicacion con el forkserver

como siempre hagamos de cuenta que salio todo bien y le llegaron los 4 bytes

ahora queda bloqueante, esperando que el server le envie 4 bytes

luego viene este bucle:

si el fork retorna error, se toma la flecha verde y se va con un exit

si retorna cero, significa que es el proceso duplicado, el hijo y esta parado en la misma zona pasando el CALL

cierra el canal donde writeaba y leia, restaura los registros, storea la posicion hitteada y sale

si es el padre, retorna el PID del proceso el fork entonces se va para aca

la parte que justo se corto es la porcion de codigo que envia el PID del hijo por el canal de comunicacion:

y se queda esperando hasta que termine el proceso con el waitpid, como el segundo argumento de waitpid no es NULL, entonces storeara un entero que avisa como fue que murio el proceso:

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *wstatus, int options);

y por ultimo envia wstatus por el canal de comunicacion

con esto puede saber si el proceso faulteo o salio aireoso

como registro el handler 199 y 198?

una de las preguntas que me genera ese codigo, es como logro tener el 199 y 198 para el, veamos el codigo de afl para sacarnos esa duda

crea un pipe y despues duplica el descriptor que se le otorgo, asignandole el 199 y 198:

afl-fuzz.c

if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed");

y mas abajo:

afl-fuzz.c

    if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");
    if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");

    close(ctl_pipe[0]);
    close(ctl_pipe[1]);
    close(st_pipe[0]);
    close(st_pipe[1]);

que hizo el cristiano este para coverage de kernel?

la pregunta que todos nos hacemos, como logro que este modo que solo funciona para coverage en procesos de user space, lograr usarlo satisfactoriamente para kernel space

levantando el kernel con virtme

luego de compilar un kernel, lo podemos levantar con virtme para correrlo virtualmente, esto nos trae la ventaja de que si crashea no vamos a perder nada, porque estamos en el virtual

sudo ./virtme-run --rw --pwd --kimg ../linux/arch/x86/boot/bzImage --memory 512M

le decimos eso y automagicamente levanta el kernel, nos da una shell de root y nos monta el disco dejandonos acceder a nuestro sistema real

dentro del kernel virtual, corremos

# ./fuzznetlink --dump --verbose

al toque nos da todos los bloques de memoria que se ejecutaron

como hizo eso?

primero habilito KCOV al momento de compilar el kernel en las zonas que me interesaban, en el tutorial el flaco se quiere centrar en sockets netlink, asi que habilito kernel coverage solo en la carpeta net:

find net -name Makefile \
    | xargs -L1 -I {} bash -c 'echo "KCOV_INSTRUMENT := y" >> {}'

en la siguiente foto podemos ver un grep que muestra en que Makefiles se activo kcov:

esto nos da la ventaja de poder aislar parcialmente las zonas que nos interesan auditar

esto le debe agregar algo a esos basic blocks para que reporten, pero por ahora no le vamos a dar importancia a eso

como obtiene las direcciones ejecutadas en kernel

en el switch para agarrar los argumentos a lo getopt tiene el procesamiento de 'd':

fuzznetlink.c:84

        case 'd':
            state->dump++;
            break;

y mas abajo vemos que si esta prendido este flag, imprime las direcciones en hexa como ya vimos:

fuzznetlink.c:242

            if (state->dump) {
                printf("0x%016lx%s\n", current_loc, "");
            }

current_loc es la direccion que tiene la posta del basic block ejecutado, si vemos el bucle entero que procesa el TRACE

fuzznetlink.c:223

        /* Read recorded %rip */
        int i;
        uint64_t afl_prev_loc = 0;
        for (i = 0; i < kcov_len; i++) {
            uint64_t current_loc = kcov_cover_buf[i + 1];
            uint64_t hash = hsiphash_static(&current_loc,
                            sizeof(unsigned long));
            uint64_t mixed = (hash & 0xffff) ^ afl_prev_loc;
            afl_prev_loc = (hash & 0xffff) >> 1;

            uint8_t *s = &afl_area_ptr[mixed];
            int r = __builtin_add_overflow(*s, 1, s);
            if (r) {
                /* Boxing. AFL is fine with overflows,
                 * but we can be better. Drop down to
                 * 128 on overflow. */
                *s = 128;
            }

            if (state->dump) {
                printf("0x%016lx%s\n", current_loc, "");
            }
        }

lo saca del kcov_cover_buf indexandolo desde 1 (eso parece raro)

kcov_cover_buf es un array de direcciones de 64 bits

fuzznetlink.c:113

    struct kcov *kcov = NULL;
    uint64_t *kcov_cover_buf = NULL;
    if (state->no_kcov == 0) {
        kcov = kcov_new();
        kcov_cover_buf = kcov_cover(kcov);
    }

me encanta la falsa orientacion a objetos que armo, divino, vamos a ver kcov_cover

kcov.c:89

uint64_t *kcov_cover(struct kcov *kcov) { return kcov->cover; }

lo unico que hay que saber de la falsa OOP es que siempre le pasa como 1er argumento un pseudo-this

kcov->cover fue cargado en el "constructor"

kcov.c:24

struct kcov *kcov_new(void)
{
    int fd = open("/sys/kernel/debug/kcov", O_RDWR);
    if (fd == -1) {
        PFATAL("open(/sys/kernel/debug/kcov)");
    }

    /* Setup trace mode and trace size. */
    int r = ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE);
    if (r != 0) {
        PFATAL("ioctl(KCOV_INIT_TRACE)");
    }

    /* Mmap buffer shared between kernel- and user-space. */
    unsigned long *cover = (unsigned long *)mmap(
        NULL, COVER_SIZE * sizeof(unsigned long),
        PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    if ((void *)cover == MAP_FAILED) {
        PFATAL("mmap(/sys/kernel/debug/kcov)");
    }
    struct kcov *kcov = calloc(1, sizeof(struct kcov));
    kcov->fd = fd;
    kcov->cover = cover;
    return kcov;
}

cover es un buffer para almacenar

kcov.c:14

#define COVER_SIZE (64 << 10)

que no es mas que una forma cheta de poner 65536

fijense que esta MAP_SHARED y los flags, eso debe ser los permisos que necesita para que se le guarden los datos

todo apunta que la parte de carga del trace se hace por aca:

kcov.c:51

void kcov_enable(struct kcov *kcov)
{
    /* reset counter */
    __atomic_store_n(&kcov->cover[0], 0, __ATOMIC_RELAXED);

    int r = ioctl(kcov->fd, KCOV_ENABLE, KCOV_TRACE_PC);
    if (r != 0) {
        PFATAL("ioctl(KCOV_ENABLE)");
    }

    /* Reset coverage. */
    __atomic_store_n(&kcov->cover[0], 0, __ATOMIC_RELAXED);
    __sync_synchronize();
}

__atomic_store_n y __sync_synchronize apuntan a que bloquean la zona de memoria mientras se carga con las direcciones

son funciones para shared memory y mutual exclusion, asi que no vamos a indagar en eso... vamos a tener en cuenta que nos cae la informacion solita

en la pagina de kcov tienen un sample casi igual, asi que vamos a tomar como que automagicamente funciona

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

#define KCOV_INIT_TRACE                     _IOR('c', 1, unsigned long)
#define KCOV_ENABLE                 _IO('c', 100)
#define KCOV_DISABLE                        _IO('c', 101)
#define COVER_SIZE                  (64<<10)

#define KCOV_TRACE_PC  0
#define KCOV_TRACE_CMP 1

int main(int argc, char **argv)
{
    int fd;
    unsigned long *cover, n, i;

    /* A single fd descriptor allows coverage collection on a single
     * thread.
     */
    fd = open("/sys/kernel/debug/kcov", O_RDWR);
    if (fd == -1)
            perror("open"), exit(1);
    /* Setup trace mode and trace size. */
    if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
            perror("ioctl"), exit(1);
    /* Mmap buffer shared between kernel- and user-space. */
    cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
                                 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if ((void*)cover == MAP_FAILED)
            perror("mmap"), exit(1);
    /* Enable coverage collection on the current thread. */
    if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC))
            perror("ioctl"), exit(1);
    /* Reset coverage from the tail of the ioctl() call. */
    __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
    /* That's the target syscal call. */
    read(-1, NULL, 0);
    /* Read number of PCs collected. */
    n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
    for (i = 0; i < n; i++)
            printf("0x%lx\n", cover[i + 1]);
    /* Disable coverage collection for the current thread. After this call
     * coverage can be enabled for a different thread.
     */
    if (ioctl(fd, KCOV_DISABLE, 0))
            perror("ioctl"), exit(1);
    /* Free resources. */
    if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
            perror("munmap"), exit(1);
    if (close(fd))
            perror("close"), exit(1);
    return 0;
}

KCOV nos tira las direcciones de los basic blocks ejecutados, si quisieramos podriamos saber la linea ejecutada pipeando a addr2line, para tener una salida similar a esta:

SyS\_read
\_\_fs/read\_write.c:562
fdget\_pos
\_\_fs/file.c:774
fget\_light
\_\_fs/file.c:746
fget\_light
\_\_fs/file.c:750
fget\_light
fs/file.c:760
fdget\_pos
\_\_fs/file.c:784
SyS\_read
fs/read\_write.c:562

pero para nuestros menesteres, nos valemos solo con el raw %rip

que da kcov?

las direcciones crudas en memoria del basic block que se ejecuto, si lo redireccionas a addr2line podes tener la linea de codigo del bloque que se ejecuto

$ ./fuzznetlink --dump --one-run < test_case.bin \
    | addr2line -e ./linux/vmlinux
[-] Running outside of AFL
linux/net/socket.c:1514
linux/net/socket.c:1500
linux/net/socket.c:1502
linux/net/socket.c:1353
linux/net/socket.c:1355

como traduce la direccion de KCOV a AFL

para pasar de un lado a otro, obtiene la direccion, le genera un hash unico a esa direccion usando el algoritmo hsiphash_static

            uint64_t current_loc = kcov_cover_buf[i + 1];
            uint64_t hash = hsiphash_static(&current_loc,
                            sizeof(unsigned long));
            uint64_t mixed = (hash & 0xffff) ^ afl_prev_loc;
            afl_prev_loc = (hash & 0xffff) >> 1;

cito al loco

But we achieved our goal - we set up a basic, yet still useful fuzzer against a kernel. Most importantly: the same machinery can be reused to fuzz other parts of Linux subsystems - from file systems to bpf verifier.

el argumento --one-run

hace una sola ejecucion del bucle, no forkea, con eso conseguimos poder tracearlo, util para tunear el fuzzer, una vez que con un archivito, llegaste a la zona que te interesa fuzzear

TIPAZOOO

que devuelve hsiphash_static

esto lo tengo que saber por simple curiosidad

me hice un clientito de la lib

para hacer esto, solo tire este codigo en donde estaba el codigo fuente del fuzznetlink original:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <ctype.h>

#include "common.h"

int main(int argc, char **argv) {
    uint64_t n, *p;

    if (argc > 1 ) {
        n = atoi(argv[1]);
        p = &n;
        printf("0x%x", hsiphash_static(p, sizeof(unsigned long)));
    }

    return 0;
}

como bien decia la documentacion por todos lados, es un algoritmo optimizado que le entran uint32 y escupe uint32, cada uno diferente al anterior por anda a saber que metodo

para compilarlo le tire un:

gcc -Wextra siphash.c -o checkhash checkhash.c -g

pero que onda, antes eran short int los valores que indexaban al buffer loco de afl, es por eso que el chabon les hace un casting feo, agarrando solo los 0xffff bytes

            uint64_t mixed = (hash & 0xffff) ^ afl_prev_loc;
            afl_prev_loc = (hash & 0xffff) >> 1;

que colisione el hash tenes que tener mucha mala suerte, pasara una vez cada muerte de obispo y de ultima en el peor de los casos te dara un falso positivo diciendo que encontro un crash

como le mete el input generado al chunk fuzzeado

                /* Load input from AFL (stdin) */
                char buf[512 * 1024];
                memset(buf, 0, 32);
                int buf_len = read(0, buf, sizeof(buf));
                if (buf_len < 0) {
                        PFATAL("read(stdin)");
                }
                if (buf_len < 5) {
                        buf_len = 5;
                }
                if (state->verbose) {
                        fprintf(stderr, "[.] %d bytes on input\n", buf_len);
                }

lee stdin, de ahi le viene el paquete crafteado, lo guarda en buf y de ahi lo deployea como parte de estructuras y fruta

                struct sockaddr_nl sa = {
                        .nl_family = AF_NETLINK,
                        .nl_groups = (buf[1] << 24) | (buf[2] << 16) |
                                     (buf[3] << 8) | buf[4],
                };
                struct iovec iov = {&buf[5], buf_len - 5};
                struct sockaddr_nl sax = {
                        .nl_family = AF_NETLINK,
                };
                struct msghdr msg = {&sax, sizeof(sax), &iov, 1, NULL, 0, 0};
                r = sendmsg(netlink_fd, &msg, 0);
                if (r != -1) {
                        char buf[8192];
                        struct iovec iov = {buf, sizeof(buf)};
                        struct sockaddr_nl sa;
                        struct msghdr msg = {&sa,  sizeof(sa), &iov, 1,
                                             NULL, 0,     0};
                        recvmsg(netlink_fd, &msg, 0);
                }

kernel-fuzzer's People

Contributors

pastaoficial avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.