- Compilar e instalar el kernel de Linux
- Modificaciones al Kernel de Linux
- Implementar llamadas al sistema (syscalls) personalizadas
- Dashboard Web para el monitoreo del uso de memoria del Sistema Operativo
- Problemas Encontrados
Antes de realizar Modificaciones al kernel e implementar syscalls, es importante preparar el entorno donde se trabajara. Se instalaran herramientas para compilar el kernel y se descargara el kernel que servirá como base para las modificaciones e implementaciones del las syscalls.
-
Estas herramientas y paquetes son de suma importancia para el kernel se compile correctamente. Si desea puede investigar para que funciona cada una. Las instalaremos con el siguiente comando
sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev fakeroot dwarves
-
El Kernel de Linux se descargara desde la pagina oficial kerne.org. Descargue la versión mas reciente longterm disponible. En esta documentación se utilizara la versión 6.6.45 pero la manera de modificar el kernel e implementar las syscalls es el mismo.
Para descargarlo compie el link de tarball y use el comando wget.
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.46.tar.xz
Ahora descomprímalo en el directorio donde quiera trabajar.
tar -xvf linux-6.6.45.tar.xz
-
Primero se debe ingresar al directorio del código fuente
cd linux-6.6.44
La configuracion que usa el kernel al momento de compilarse esta dado por el archivo .config. Este archivo se tiene que crear. Para crear este archivo se puede utilizar
make menuconfig
que mostrara una interfaz grafica en la que se puede seleccionar y ajustar varias opciones y caracteristicas del kernel.
Sin embargo para simplificar el procedimiento, se utilizara el archivo .config que utilizo el kernel actual para compilarse y que se encuntra actualmente instalado.
Con el siguiente comando se copiara el archivos .config a el directorio donde usted se encuentra.
cp -v /boot/config-$(uname -r) .config
Sin embargo, este contiene tiene opciones, caracteristicas, modulos y drivers que no se usan en nuestro sistema operativo, ya que este esta hecho para compilar un kernel para diferentes tipos de hardware. Debido a eso, el proceso de compilacion tardaria demasiado, asi que lo que haremos sera modificarlo para que solo compile las opciones, caracteristicas, modulos y drivers que solo esta usando nuestro sistema operativo. Asi el proceso de compilacion sera mas rapido. Cabe decir que si usted desea usar el kernel modificado funcione correctamente en otras computadora, tendria que usar el archivo original o crear uno usando la herramienta anteriormente mencionada. Para modificarlos usaremos el siguiente comando
make localmodconfig
Otro problema es el que al añadir un nuevo modulo o archivos de codigo, estos no cuentan con una firma digital que confirman que son autenticos. Asi que para evitar errores desactivaremos las claves que se encuentran en el archivo .config que verifican esa informacion. Utilizamos el siguiente comando
scripts/config --disable SYSTEM_TRUSTED_KEYS scripts/config --disable SYSTEM_REVOCATION_KEYS scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEYS "" scripts/config --set-str CONFIG_SYSTEM_REVOCATION_KEYS ""
También se pude llegar a tener problemas con BPF/BTF. Se recomienda desactivarlo usando el siguiente comando:
# Desactivar las opciones relacionadas con BPF scripts/config --disable CONFIG_BPF scripts/config --disable CONFIG_BPF_SYSCALL scripts/config --disable CONFIG_BPF_JIT scripts/config --disable CONFIG_BPF_JIT_ALWAYS_ON scripts/config --disable CONFIG_BPF_JIT_DEFAULT_ON # Desactivar las opciones relacionadas con BTF y debug scripts/config --disable CONFIG_PAHOLE_HAS_BTF_TAG scripts/config --disable CONFIG_DEBUG_INFO_BTF scripts/config --disable CONFIG_DEBUG_INFO_BTF_MODULES
-
Para compilar el kernel usaremos fakeroot. Utilizar fakeroot es necesario por que nos permite ejecutar el comando make en un entorno donde parece que se tiene permisos de superusuario para la manipulación de ficheros. Es necesario para permitir a este comando crear archivos (tar, ar, .deb etc.) con ficheros con permisos/propietarios de superusuario.
fakeroot make
Se puede acelerar la compilacion del kernel utilizando varios nucleos de la CPU con el siguiente comando
fakeroot make -jN
Donde N/ es el numero de núcleos que desee usar.
Al finalizar la compilacion utilizaremos el siguiente comando para ver si ocurrio algun error dunrante la compilacion. Si el resultando del comando es 0, signfica que no hubo errores y podemos instalar el kernel. El comando es
echo $?
Si desea o requiere eliminar las configuraciones o archivos creados durante la configuración y compilación del kernel, puede usar los siguientes comandos. Tomar en cuenta que también se borrara el archivo .config que se creo anteriormente.
make clean make mrproper
-
La instalacion se divide en dos partes
- Instalar los módulos del Kernel
- Instalar el Kernel mismo
Utilizaremos el siguiente comando
sudo make modules_install
Utilizaremos el siguiente comando
sudo make install
Por ultimo será necesario reiniciar nuestra computadora.
sudo reboot
-
Si el kernel que instalamos es de una versión anterior a nuestro kernel actual del sistema operativo, al iniciar siempre se eligiera automáticamente el que tenga la versión mayor. Para seleccionar la versión del kernel que queremos utilizar podemos acceder al GRUB. Para acceder al GRUB debe investigar que tecla utiliza su distribución y usarla en el arranque del sistema.
Ya que podemos compilar nuestro kernel, ahora aprenderemos a modificarlos. Las modificaciones que se explican en esta sección serán simples. No reflejan la gran cantidad y complejidad de modificaciones que se pueden realizar. Pero serviran para introducirnos en el kernel de nuestro sistema operativo.
-
Se imprimirá un mensaje personalizado que se guardara en los logs de nuestro kernel.
Desde la raiz del codigo fuente abriremos el siguiente archivo
nano init/main.c
Ya en el archivos, buscaremos "void start_kernel". Esta es un metodo que se ejecuta al iniciar el kernel. Lo que haremos sera un agregar un "printk" al inicio del metodo, con cualquier mensaje que queramos.
Este mensaje lo podremos ver con el siguiente comando
dmesg
-
Modificar el nombre de nuestro Kernel.
Desde la raiz del codigo fuente abriremos el siguiente archivo
nano include/linux/uts.h
Dentro del archivo podremos encontrar facilmente "UTS_SYSNAME". Que es donde se almacena el nombre de nuestro Kernel. Podemos colocar cualquiera que queramos.
Ahora veremos como crear una nueva syscall e implementarla. También como usarla a nivel de usuario en un programa en lenguaje C. El procedimiento es el mismo para cualquier syscall, lo único que cambia es el código propio de la syscall. Teniendo eso en cuenta, se explicara detalladamente el proceso para la primera sycall y será el mismo para cualquiera.
Ya que el objetivo es: implementar una syscall, no se explicara a fondo el código propio de la syscall.
-
Esta syscall devuelve la hora actual en segundos desde el epoch linux.
-
Crear una carpeta en el directorio del código fuente con el nombre que tendrá la syscall y moverse al directorio creado.
mkdir currenttimeseg cd currenttimeseg
-
Crear un archivo .c con el nombre de la syscall en el directorio actual.
nano currenttimeseg.c
Dentro de este archivo hira el codigo propio de la syscall. El codigo para la syscall es
#include <linux/kernel.h> #include <linux/syscalls.hc #include <linux/time.h> SYSCALL_DEFINE0(currenttimeseg) { struct timespec64 ts; ktime_get_real_ts64(&ts); return ts.tv_sec; }
Se utiliza la funcion interna del kernel "ktime_get_real_ts64" para obtener la hora actual en segundos. Tambien un struct de tipo timespec64 para guardarla y retornar tv_sec, que corresponde al tiempo en segundos.
-
Crear un Makefile en el directorio actual.
nano Makefile
Tendrá la siguiente linea.
obj-y := urrenttimeseg.o
Esto es para garantizar que el archivo currenttimeseg.c se compile e incluya en el codigo fuente del Kernel.
-
Añadir el directorio currenttimeseg/ al Makefile del Kernel.
Primero volver al directorio del codigo fuente del kernel y abrir el archivo Makefile.
cd .. nano Makefile
Buscar "core-y" dentro del archivo. Encontraremos la siguiente linea
En esta linea agregaremos el directorio que creamos anteriormente: currenttimeseg/. Tiene que haber un espacio entre cada directorio que se agrega a la linea.
-
Agregar la syscall a la tabla de syscalls del sistema.
Si está en un sistema de 32 bits, deberá cambiar 'syscall_32.tbl'. Para 64 bits, cambie 'syscall_64.tbl'
Abra el archivo con el siguiente comando
nano arch/x86/entry/syscalls/syscall_64.tbl
En la ultima linea del archivo agregar la siguiente linea
548 64 currenttimeseg sys_currenttimeseg
Tiene que ser el mismo nombre de la syscall. Guarde y salga del archivo.
-
Agregar la syscall al archivo de encabezado de syscalls.
Abra el archivo con el siguiente comando.
nano include/linux/syscalls.h
Añadir la siguiente linea al final del documento antes del #endif.
asmlinkage long sys_currenttimeseg(void);
Guardar y salir de archivo. Estos son todos los paso para implentar una syscall al kernel. Ahora solo tiene que compilar el kernel.
-
-
Esta syscall devuelve el tiempo de actividad del sistema desde el ultimo reinicio.
-
#include <linux/kernel.h> #include <linux/syscalls.h> #include <linux/timekeeping.h> SYSCALL_DEFINE0(systemuptimeseg) { struct timespec64 uptime; ktime_get_boottime_ts64(&uptime); return uptime.tv_sec; }
-
obj-y := systemuptimeseg.o
-
549 64 systemuptimeseg sys_systemuptimeseg
-
asmlinkage long sys_systemuptimeseg(void);
-
-
Esta syscall recupera los últimos 5 mensajes del log del kernel
-
#include <linux/kernel.h> #include <linux/syscalls.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/uaccess.h> #define LOG_FILE_PATH "/var/log/kern.log" #define LOG_LINES 5 #define BUFFER_SIZE 2048 SYSCALL_DEFINE1(lastkernlogs, char __user *, buffer) { struct file *file; loff_t pos = 0; char *kernel_buffer; int size = 0; kernel_buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL); if (!kernel_buffer) return -ENOMEM; file = filp_open(LOG_FILE_PATH, O_RDONLY, 0); if (IS_ERR(file)) { kfree(kernel_buffer); return PTR_ERR(file); } pos = vfs_llseek(file, -BUFFER_SIZE, SEEK_END); size = kernel_read(file, kernel_buffer, BUFFER_SIZE, &pos); filp_close(file, NULL); if (size >= 0) { int count_lines = 0; int pos_buffer = 0; for (int i = BUFFER_SIZE-1 ; i >= 0; i-- ){ if (kernel_buffer[i] == '\n'){ count_lines++; if (count_lines > LOG_LINES){ pos_buffer = i; break; } } } size = BUFFER_SIZE - (pos_buffer + 1); if (copy_to_user(buffer, &kernel_buffer[pos_buffer + 1], size)) { kfree(kernel_buffer); return -EFAULT; } } kfree(kernel_buffer); return size; }
-
obj-y := lastkernlogs.o
-
550 64 lastkernlogs sys_lastkernlogs
-
asmlinkage long sys_lastkernlogs(char __user *buffer);
-
-
El metodo que se usa para encriptar y desencriptar es por XOR, lo que hace que las 2 syscalls utilizan la misma lógica para encriptar y desencriptar, y también reciben los mismo 4 parámetros (path_input, path_output, cantidad_threads, path_key), entonces se crea una función que reciba estos 4 parámetros y solo se invoca en las definiciones de las syscalls.
SYSCALL_DEFINE4(my_encrypt, char __user *, path_input, char __user *, path_output, int, no_threads, char __user *, path_key) { return function_syscall(path_input, path_output, no_threads, path_key); } SYSCALL_DEFINE4(my_decrypt, char __user *, path_input, char __user *, path_output, int, no_threads, char __user *, path_key) { return function_syscall(path_input, path_output, no_threads, path_key); }
-
Funcion 'function_syscall'
Esta función es la encargada de recibir los parámetros de la syscall y encriptar o desencriptar dependiendo si el input esta o no esta encriptado.
int function_syscall(char __user *path_input, char __user *path_output, int no_threads, char __user *path_key);
-
Implementación de la función 'function_syscall'
-
Punteros del nivel de usuario
Los punteros que se obtienen por lo parámetros apuntan a dirección a nivel de usuario, es decir, que no se pueden utilizar a nivel de kernel. La solución para esto es obtener el tamaño de las cadenas y realizar una copia. Para obtener el tamaño se utiliza "strnlen_user", que recibe como parámetros, un puntero de nivel de usuario y el tamaño máximo que leerá si no encuentra el final de la cadena(para evitar errores). También se utilizar "copy_from_user" para crear la copia a un puntero de nivel del kernel.
int max_length = 1000; //Path input long length = strnlen_user(path_input, max_length); if (length == 0 || length > max_length) { printk("Error: Invalid path_input\n"); return -EINVAL; } char *path_input_ = kmalloc(length, GFP_KERNEL); if (copy_from_user(path_input_, path_input, length)) { return -EFAULT; }
-
División de fragmentos
Debido a que hasta en tiempo de ejecucion se sabe en cuantos fragmentos se dividira la entrada, se utiliza un doble puntero char **, esto para crear en tiempo de ejecucion una lista de apuntadores y cada apuntador apunta a un fragmentos. La lectura del archivo input y calculo de los fragmentos se realiza con el siguientes codigo:
struct file *file_input; file_input = filp_open(path_input_, O_RDONLY, 0); if (IS_ERR(file_input)) { printk(KERN_ERR "Error opening input file\n"); return PTR_ERR(file_input); } loff_t size_file = vfs_llseek(file_input,0,SEEK_END); loff_t pos = vfs_llseek(file_input, 0, SEEK_SET); printk(KERN_INFO "File size: %lld\n", size_file); int size_fragment = size_file / no_threads; int size_last_fragment = size_file % no_threads; printk(KERN_INFO "Fragment size: %d\n", size_fragment); printk(KERN_INFO "Last fragment size: %d\n", size_last_fragment+size_fragment); char **fragments_text = kmalloc_array(no_threads, sizeof(char *), GFP_KERNEL); if (!fragments_text) { ret = -ENOMEM; goto final; } for (int i = 0; i < no_threads; i++) { int size = size_fragment; if (i == (no_threads - 1)) { size = size_fragment + size_last_fragment; } fragments_text[i] = kmalloc_array(size, sizeof(char), GFP_KERNEL); if (!fragments_text[i]) { ret = -ENOMEM; goto final; } } for (int i = 0; i < no_threads; i++) { int size = size_fragment; if (i == (no_threads - 1)) { size = size_fragment + size_last_fragment; } kernel_read(file_input, fragments_text[i], size, &pos); } filp_close(file_input, NULL);
-
La lectura de la key
Se hace desde el archivo donde se encuentra la llave hacia un puntero char * con el siguiente codigo:
struct file *file_key; file_key = filp_open(path_key_, O_RDONLY, 0); if (IS_ERR(file_key)) { printk(KERN_ERR "Error opening key file\n"); return PTR_ERR(file_key); } loff_t size_key = vfs_llseek(file_key,0,SEEK_END); pos = vfs_llseek(file_key, 0, SEEK_SET); printk(KERN_INFO "Key size: %lld\n", size_key); char *key = kmalloc_array(size_key, sizeof(char), GFP_KERNEL); if (!key) { ret = -ENOMEM; goto final; } kernel_read(file_key, key, size_key, &pos); filp_close(file_key, NULL); printk(KERN_INFO "File Key read\n");
-
Estructura para cada hilo
Se creo una estructura con la informacion necesaria para que cada hilo pueda encriptar/desencriptar su fragmento.
struct args_thread { struct completion *thread_completed; int no_thread; char *fragment_text; int size_fragment; char *key; int size_key; };
Se utiliza la estructura completion definida por el kernel, que sirve para que el hilo principal espere a que los demás hilos terminen su tarea. El hilo es el encargado de enviar una señal al hilo principal a través de la estructura completion.
-
Funcion para los hilos
Con la informacion de la estructura args_thread es bastante sencillo encriptar/desencriptar.
int function_thread(void* arg) { struct args_thread *args = (struct args_thread *)arg; printk(KERN_INFO "Thread created %d\n", args->no_thread); //********************************************************************** //Encriptar fragmento por XOR con la clave for (int i = 0; i < args->size_fragment; i++) { args->fragment_text[i] = args->fragment_text[i] ^ args->key[i % args->size_key]; } //********************************************************************** complete(args->thread_completed); // Marca la tarea como completada return 0; }
-
Espera del hilo principal
El hilo principal necesita esperar a lo demas hilos para escribir el encriptado/desencriptado en el archivo de salida.
Para la espera se utiliza la estructura completion.
for (int i = 0; i < no_threads; i++) { wait_for_completion(args_threads[i]->thread_completed); printk(KERN_INFO "Thread %d completed\n", i); }
-
Escribir en el archivo de salida
La escritura en el archivo es bastante simple una vez los hilos terminaron su tarea.
struct file *file_output; file_output = filp_open(path_output_, O_WRONLY | O_CREAT, 0777); if (IS_ERR(file_output)) { printk(KERN_ERR "Error opening output file\n"); return PTR_ERR(file_output); } pos = vfs_llseek(file_output, 0, SEEK_SET); for (int i = 0; i < no_threads; i++) { kernel_write(file_output, fragments_text[i], args_threads[i]->size_fragment, &pos); } filp_close(file_output, NULL);
-
Liberacion de memoria
Despues de escribir en el archivo de salida es necesario liberar toda la memoria utiliza para que pueda ser usada posteriormente por otros procesos.
cleanup_partial: // Liberar memoria en caso de error o al finalizar for (int i = 0; i < no_threads; i++) { if (args_threads[i]) { kfree(args_threads[i]->thread_completed); kfree(args_threads[i]->fragment_text); kfree(args_threads[i]); } } kfree(threads); kfree(args_threads); final: kfree(path_input_); kfree(path_output_); kfree(path_key_); for (int i = 0; i < no_threads; i++) { if (fragments_text){ kfree(fragments_text[i]); } } kfree(fragments_text); kfree(key);
-
-
-
Esta syscall devuelve como esta distribuido el uso de la memoria física. Toda la información de la memoria se puede obtener utilizando la estructura struct sysinfo y usando 2 funciones para recuperar los datos y guardarlos en la estructura. La memoria libre, utilizada y cache se calcula de la siguiente forma:
struct sysinfo mem_info; si_meminfo(&mem_info); si_swapinfo(&mem_info); //Memoria libre en KB info[0] = mem_info.freeram << (PAGE_SHIFT - 10); //Memoria usada en KB unsigned long available = si_mem_available(); info[1] = (mem_info.totalram - available) << (PAGE_SHIFT - 10); //Memoria cacheada en KB info[2] = (global_node_page_state(NR_FILE_PAGES) - total_swapcache_pages() - mem_info.bufferram) << (PAGE_SHIFT - 10); //Memoria total en KB info[3] = mem_info.totalram << (PAGE_SHIFT - 10);
-
Esta llamada recupera cuenta de la memoria swap esta siendo utilizada y cuanta se encuentra libre. Toda la información se guarda en la estructura struct sysinfo utilizando 2 funciones para obtenerla. Se calculan de la siguiente forma:
struct sysinfo mem_info; si_meminfo(&mem_info); si_swapinfo(&mem_info); //Swap usado en paginas info[0] = mem_info.totalswap - mem_info.freeswap; //Swap libre en paginas info[1] = mem_info.freeswap; //Swap total en paginas info[2] = mem_info.totalswap;
-
Es syscall recupera la cantidad de fallos de paginas menores y mayores. Se utiliza un puntero de tipo long para guardar el array de toda la información relacionada de la memoria virtual. Se utiliza la función all_vm_events para recuperarla y guardarla en el puntero. Se obtiene de la siguiente forma:
unsigned long *info = kmalloc_array(NR_VM_EVENT_ITEMS, sizeof(unsigned long), GFP_KERNEL); all_vm_events(info);
Los fallos menores se encuentran en la posicion 23 y los mayores en la posicion 24 del array.
-
Esta llamada recupera la cantidad de paginas que se encuentran en uso constante (Activas) y las que no se encuentran en uso o no se han usado en un determinado tiempo. Se utiliza la función global_node_page_state(), esta se encarga de recuperar varios datos relacionados con la memoria, incluyendo la memoria virtual. Se recupera de la siguiente forma:
//Obtener informacion de paginas unsigned long pages[NR_LRU_LISTS]; for (int lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) pages[lru] = global_node_page_state(NR_LRU_BASE + lru); //Paginas Activas info[0] = pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]; //Paginas Inactivas info[1] = pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE];
-
Esta syscall recupera los 5 proceso que mas utilizan memoria física. Se recorre toda la estructura de datos que guarda todos los procesos. Se utiliza un simple algoritmo que comprueba cuales son los mayores procesos que utilizan memoria y se guardan en un array de 5 elementos. Se utiliza una estructura personalizada para guardar los 5 procesos.
struct proc_info { char name[16]; long pid; unsigned long pct_usage; unsigned long mm_rss; }; struct proc_info *procs_info = kmalloc_array(top_procs, sizeof(struct proc_info), GFP_KERNEL); //Obtener informacion de procesos struct task_struct *task; unsigned long mm_rss = 0; for_each_process(task){ if (!task->mm) { continue; } mm_rss = get_mm_rss(task->mm); for (int i = 0; i < top_procs; i++) { if (mm_rss > procs_info[i].mm_rss) { //Correr hacia abajo for (int j = top_procs - 1; j > i; j--) { procs_info[j] = procs_info[j - 1]; } //Insertar nuevo proceso procs_info[i].pid = task->pid; strncpy(procs_info[i].name, task->comm, 16); procs_info[i].pct_usage = (mm_rss * 1000) / mem_info.totalram; procs_info[i].mm_rss = mm_rss; break; } } }
El Dashboard Web está compuesto por cinco gráficas que muestran en tiempo real el uso de la memoria del sistema. Esta aplicación, desarrollada con React, consume una API REST construida en C utilizando el framework Ulfius HTTP.
Cada gráfica está asociada a un endpoint específico en la API REST. Estos endpoints interactúan con syscalls personalizadas, implementadas en el kernel del sistema, para obtener y procesar información detallada sobre el uso de la memoria.
Gráfica | Descripción | Endpoint | Syscalls | Representación Visual |
---|---|---|---|---|
Uso de Memoria Física (gráfico de pie) | Proporciona una descripción general de cómo se utiliza la memoria física, lo que facilita la comprensión de la distribución general de los recursos de memoria. La gráfica muestra: memoria usada, memoria libre y memoria en caché | Tipo GET. /use_mem | Uso de Memoria | Ver |
Uso de Memoria a lo largo del tiempo (gráfico de líneas) | Mostrará cómo el uso de la memoria evoluciona con el tiempo, con líneas separadas para la memoria física y swap, lo que permite a los usuarios identificar picos en el uso de la memoria o períodos de presión de la memoria. | Tipo GET. /use_swap | - Uso de Memoria - Swap |
- Ver Imagen 1 - Ver Imagen 2 |
Fallos de páginas (gráfico de barras) | Muestra la cantidad de los fallos de página menores y fallos de página mayores | Tipo GET. /fault_pages | Cantidad de Fallos de Páginas | - Ver Imagen 1 - Ver Imagen 2 |
Páginas Activas e Inactivas (gráfico de pie) | Muestra las páginas de memoria activas e inactivas en el sistema. Proporciona una vista de cuánta memoria del sistema se utiliza activamente frente a la que está marcada como inactivo y potencialmente disponible | Tipo GET. /use_pages | Páginas Activas e Inactivas | Ver |
Top 5 Procesos con Mayor Uso de Memoria (gráfico de barras) | Muestra los 5 procesos que consumen más memoria, mostrando el nombre del proceso, el PID y el porcentaje del uso de memoria. Permite a los usuarios identificar qué procesos consumen más memoria, lo que ayuda a diagnosticar procesos fuera de control. | Tipo GET. /use_mem_top_procs | Procesos que más memoria utilizan | Ver |
-
Es importante que el nombre de nuestra syscall sea el mismo en todos los archivos en la que agregamos ya que si no fuera el mismo habrá errores de compilación.
- Solución: siempre revisar con detenimiento si el nombre de nuestra funcion se encuentra bien escrito en todos los archivos.
-
No quitar las claves de firmas digitales del archivo .config ocasiona que no podamos agregar archivos nuevos y producirá errores en la compilación.
- Solución: Verificar que hemos eliminar las clases de firmas digitales en el archivos .config o firmar digitalmente nuestro archivos de codigo. Para eliminar las claves utilizar
scripts/config --disable SYSTEM_TRUSTED_KEYS scripts/config --disable SYSTEM_REVOCATION_KEYS scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEYS "" scripts/config --set-str CONFIG_SYSTEM_REVOCATION_KEYS ""
-
Errores en la compilación debido a que no se puedan crear archivos (tar, ar, .deb etc.) con ficheros con permisos/propietarios de superusuario.
- Solución: utilizar fakeroot para evitarlo ya que este crea un entorno donde parece que se tiene permisos de superusuario para la manipulación de ficheros.
fakeroot make
-
Error al intentar utilizar los apuntadores de espacio de usuario en el espacio de kernel.
- Solución: Realizar una copia a apuntadores del espacio de kernel para manipular la información.
char *path_input_ = kmalloc(length, GFP_KERNEL); if (copy_from_user(path_input_, path_input, length)) { return -EFAULT; }
-
Problemas al leer y copiar los datos del archivo a la memoria.
*Solucion: Utilizar la funcion kernel_read, ya que esta cuenta con privilegios para copiar a espacio de kernel.
kernel_read(file_key, key, size_key, &pos);
-
Problemas con el BPF/BTF al compilar el kernel. Se puede llevar a tener problemas al usar el archivo .config del kernel actualmente instalada.
- Solución: Desactivarlo usando
# Desactivar las opciones relacionadas con BPF scripts/config --disable CONFIG_BPF scripts/config --disable CONFIG_BPF_SYSCALL scripts/config --disable CONFIG_BPF_JIT scripts/config --disable CONFIG_BPF_JIT_ALWAYS_ON scripts/config --disable CONFIG_BPF_JIT_DEFAULT_ON # Desactivar las opciones relacionadas con BTF y debug scripts/config --disable CONFIG_PAHOLE_HAS_BTF_TAG scripts/config --disable CONFIG_DEBUG_INFO_BTF scripts/config --disable CONFIG_DEBUG_INFO_BTF_MODULES
-
Problemas con los CORS en la api en c. El autor del framework recomendó configurar los CORS para cada endpoint ya que configurarlos para todos es una tarea muy compleja.
- Solución: Configurar CORS para cada endpoint
// Configure CORS u_map_put(response->map_header, "Access-Control-Allow-Origin", "*");
-
Problemas al interpretar el valor de la estructura struct sysinfo para obtener la información sobre la memoria. Los valores que maneja la estructura esta dado en paginas y para convertirlos a KB hay que multiplicarlo por 4 ya que cada pagina es un tamaño de 4KB.
- Solución: Desplazar 2 bit a la izquierda o multiplicarlos por 4
//Memoria libre en KB info[0] = mem_info.freeram << (PAGE_SHIFT - 10);
-
Tipo de dato incorrecto la crear el JSON para el response de la request en la api de c. En la función para crear un objeto JSON se debe indicar que tipo de dato se espera para convertirlo. Se cometió el error de indicar que se esperaba un dato de tipo float pero el dato era Integer, entonces al momento de convertirlo hubo un error de segmento de memoria porque el dato espera es mas grande que Integer.
- Solución: parsear el dato de Integer a double para evitar el error de segmento
json = json_pack("{s:f, s:f, s:f, s:f}", "free", ((double)_info[0])/1024, "used", ((double)_info[1])/1024, "cached", ((double)_info[2])/1024, "total", ((double)_info[3])/1024);