Linux-Kernel: Zugriff auf Kernelspeicher

Mit jeder neuen Kernel-Release werden auch eine ganze Reihe von Sicherheitslücken im Kernel korrigiert. Das betraf vor kurzem eine Schwachstelle in der Bluetooth-Implementierung des Kernels, über die ein lokaler Angreifer Kernelspeicher auslesen konnte.

Die verantwortlichen Programmierfehler befinden sich in den Dateien “net/bluetooth/l2cap_sock.c” und “net/bluetooth/rfcomm/sock.c”. Beide verwenden die folgenden Strukturen, die das HCI-Handle und die Device-Klasse speichern:

struct l2cap_conninfo { __u16 hci_handle; __u8 dev_class[3];
};
struct rfcomm_conninfo { uint16_t hci_handle; uint8_t dev_class[3];
};

Die Variable “cinfo” wird in “l2cap_sock_getsockopt_old()” beziehungsweise “rfcomm_sock_getsockopt_old” als Variable dieses Typs deklariert. In beiden Dateien wird die in den Strukturen gespeicherte Bluetooth-Connection-Information mit folgenden Zeilen vom Kernelspace in den Userspace kopiert, sodass sich Userspace-Programme um das Bluetooth-Device kümmern können:

if (copy_to_user(optval, (char *) &cinfo, len)) err = -EFAULT;

Da der Kernel die höchsten Berechtigungen auf dem System besitzt, ist es bei solchen Kopieraktionen immer wichtig, dass nicht versehentlich unkontrollierter Kernelspeicher in den Userspace gelangt. Dies könnte leicht passieren, wenn eine Variable nicht initialisiert wurde (und weil C das nicht automatisch macht). Beispielsweise wäre es ungünstig, wenn einige der “l2cap_conninfo”- oder “rfcomm_conninfo”-Felder vor dem Kopieren mit “copy_to_user()” nicht initialisiert würden. Dies ist aber auch nicht der Fall, denn in “l2cap_sock.c” (wie auch in “sock.c”) werden die einzelnen Einträge korrekt initialisiert:

cinfo.hci_handle = chan->conn->hcon->handle;
memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3);

Trotzdem versteckt sich hier eine Sicherheitslücke, denn Strukturen sind im allgemeinen nicht nur Ansammlungen von Datentypen, sondern auch im Speicher speziell ausgerichtet beziehungsweise gefüllt. So ist es auf 32-Bit-Systemen aus Performance-Gründen besser, die einzelnen Variablen an 4-Byte-Grenzen im Speicher auszurichten. Folgendes Beispiel demonstriert dies:

#include <string.h>
#include <stdio.h>
struct test {
char a;
char b;
int c;
};
int main(void)
{ printf("sizeof(struct test)=%ld\n", sizeof(struct test));
}

Obwohl die Datentypen hier eigentlich nur 1+1+4=6 Bytes belegen sollten, wird das Programm beispielsweise (dies hängt von Prozessor, Betriebssystem, Compiler ab) 8 Bytes liefern, weil 2 Padding-Bytes eingesetzt wurden. Und genau das passiert auch bei den Strukturen “l2cap_conninfo” und “rfcomm_conninfo”, die mit einem Padding-Byte aufgefüllt werden. Obwohl die Struktur-Mitglieder korrekt initialisiert werden, bleibt daher ein Byte trotzdem uninitialisiert. Dieses enthält deshalb Kernel-Speicherdaten, und wird in den Userspace kopiert: Es geschieht genau, was eigentlich nicht vorkommen sollte.

Der einfache Fix für dieses Problem besteht darin, die gesamte Struktur via “memset()” auf Null zu setzen:

memset(&cinfo, 0, sizeof(cinfo));

Damit werden sowohl die Variablen als auch die Padding-Bytes auf Null gesetzt. Dieses Patch wurde Mitte Juni in den Kernelzweig 2.6 eingefügt.

Nach oben