seq_file
The seq_file (Sequence File) is a standard Linux kernel mechanism used to generate large, dynamic, or complex datasets (like process status or network statistics) into a sequential, human-readable text stream without requiring massive pre-allocated buffers.
1. The Structures
seq_file relies on two interdependent structures defined in include/linux/seq_file.h. These are the primary targets when manipulating kernel memory.
struct seq_file (State Tracker)
This structure tracks the current position and buffer state during a read operation.
struct seq_file {
char *buf; // The 4KB buffer for output text
size_t size; // Total buffer size
size_t from; // Current read offset in buffer
size_t count; // Data remaining in buffer
loff_t index; // Current iterator index
const struct seq_operations *op; // Link to iteration logic
void *private; // Optional driver-specific data
// ... (additional alignment/locking fields)
};
struct seq_operations (Iteration Logic)
This is a dispatch table containing pointers to the functions that handle the lifecycle of the data traversal.
struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos); // Initialize/Lock
void (*stop) (struct seq_file *m, void *v); // Cleanup/Unlock
void * (*next) (struct seq_file *m, void *v, loff_t *pos); // Advance
int (*show) (struct seq_file *m, void *v); // Format to buffer
};
2. The Workflow
When a user-space process opens a virtual file (e.g., /proc/self/stat), the kernel executes a controlled sequence:
-
Open: The kernel allocates a
seq_filestructure in the slab cache (kmalloc) and links it to the specificseq_operationstable for that file. -
Iterate:
start(): Prepares the internal data source (e.g.,task_struct).show(): Formats the current data into theseq_file->buf.next(): Moves to the next data element.
-
Read/Copy: The formatted text is copied from the kernel’s
bufto the user-space buffer requested by theread()syscall. -
Close: Resources are freed.
Note
seq_fileis the generic struct that moves data from userspace. However,seq_operatioinsis the specific struct for reading/proc/self/statFor More details : https://www.kernel.org/doc/html/latest/filesystems/seq_file.html
3. Exploitation Context: Why we Spray seq_file
In a kernel exploitation scenario, seq_file is useful for heap manipulation for three reasons:
-
Heap Grooming: Because
seq_fileobjects arekmalloc‘d in direct response toopen()syscalls, an attacker can precisely fillkmalloc-32orkmalloc-64slab slots. This allows the attacker to create a “predictable heap layout,” placing their own controlled data adjacent to vulnerable structures. -
KASLR Bypass : The
seq_file->oppointer is a constant address pointing to the kernel’s read-only data segment. By using an read primitive to leak this pointer, an attacker can leak kernel mapped address. -
Control Flow Hijack: If an attacker gains an arbitrary write primitive, they can overwrite the
seq_file->oppointer to point to a “fake”seq_operationstable created in controlled memory. When the kernel next attempts to iterate (e.g., via areadorcloseon the file), it will jump to the attacker-supplied function pointers.
4. How to ?
#define SPRAY_COUNT 0x200
static int spray[0x200];
for (int i = 0; i < SPRAY_COUNT; i++) {
spray[i] = open("/proc/self/stat", O_RDONLY);
if (spray[i] == -1) {
perror("/proc/self/stat");
}
}