wandres.dev
LA FRONTERA CON EL USUARIO · copy_*_user, capabilities

La frontera usuario↔kernel

copy_to_user y copy_from_user: cómo el kernel mueve datos a través de la frontera de forma segura, y por qué jamás debe confiar en un puntero del espacio de usuario.

⏱ 12 min

Cuando un programa hace una syscall con datos (leer, escribir, ioctl), esos datos viven en el espacio de usuario, y el kernel debe traerlos o enviarlos cruzando la frontera. Hacerlo mal no es un bug cualquiera: es un agujero de seguridad que compromete todo el sistema. Este es uno de los temas más críticos del kernel.

🎯 Al terminar esta lección sabrás
  • Por qué no puedes dereferenciar punteros de usuario.
  • copy_from_user y copy_to_user.
  • get_user y put_user.
  • La regla de oro: nunca confíes en el usuario.

Por qué no puedes dereferenciar un puntero de usuario

Cuando tu código de kernel recibe un char *buf que viene de un programa, ese puntero apunta al espacio de usuario. Dereferenciarlo directamente es peligroso por tres razones:

  • La dirección podría ser inválida o maliciosa (el programa podría pasar un puntero a memoria del kernel para engañarte).
  • La página podría no estar presente (paginada a disco), causando un fallo en un contexto donde no puede resolverse.
  • Sería una vulnerabilidad: un programa sin privilegios podría leer o escribir memoria del kernel.

Por eso el kernel prohíbe dereferenciar punteros de usuario directamente. Los marca __user (nivel 9.2) y sparse te avisa si lo intentas.

copy_from_user y copy_to_user

Las funciones seguras que cruzan la frontera, comprobando cada acceso:

/* traer datos DEL usuario al kernel */
char kbuf[128];
if (copy_from_user(kbuf, user_ptr, len))   /* devuelve nº de bytes NO copiados */
	return -EFAULT;                     /* si no es 0, la dirección era mala */

/* enviar datos DEL kernel al usuario */
if (copy_to_user(user_ptr, kbuf, len))
	return -EFAULT;

Devuelven el número de bytes que no pudieron copiarse: 0 significa éxito. Cualquier otra cosa, devuelves -EFAULT. Para valores simples (un int), get_user/put_user son atajos.

Nunca confíes en el espacio de usuario: la regla que sostiene la seguridad

Esta es una de las mentalidades más importantes de toda la programación de kernel, y por extensión, de la seguridad informática. Todo dato que cruza desde el espacio de usuario es hostil hasta que se demuestre lo contrario. Un programa podría pasarte un puntero a memoria del kernel esperando que lo copies a un sitio que él pueda leer (fuga de datos), o un tamaño gigante para desbordar tu buffer, o valores diseñados para romper tu lógica. El kernel es el guardián con privilegio total; si baja la guardia una sola vez, un programa sin privilegios escala a control total del sistema. Por eso: (1) nunca dereferencies punteros de usuario directamente — usa copy_*_user, que valida la dirección; (2) valida cada tamaño y cada valor antes de usarlo (¿cabe en mi buffer? ¿está en rango?); (3) asume malicia, no descuido. Innumerables vulnerabilidades del kernel (CVEs) han sido exactamente esto: un punto de la frontera donde se confió en el usuario. Interiorizar esta desconfianza sistemática no es paranoia: es el oficio.

⚔️ Cruza la frontera con seguridad
  1. Escribe una función que reciba un __user buffer y lo copie al kernel con copy_from_user, devolviendo -EFAULT si falla.
  2. Escribe la inversa con copy_to_user.
  3. Añade validación de len: rechaza tamaños que no quepan en tu buffer.
  4. Investiga un CVE real del kernel causado por confiar en un puntero o tamaño de usuario.