--- linux-orig/fs/proc/cmdline.c +++ linux/fs/proc/cmdline.c @@ -3,6 +3,7 @@ #include #include #include +//#include static int cmdline_proc_show(struct seq_file *m, void *v) { @@ -22,9 +23,69 @@ .release = single_release, }; +static DEFINE_SPINLOCK(cmdline_lock); + +static int cmdline_hidden_proc_show(struct seq_file *m, void *v) +{ + char *cmdline = 0; + size_t len; + + /* Hidden data can only be read once! */ + spin_lock(&cmdline_lock); + if (hidden_command_line) { + cmdline = hidden_command_line; + hidden_command_line = 0; + } + spin_unlock(&cmdline_lock); + + /* No hidden command line found, or already read. */ + if (!cmdline) { + seq_printf(m, "\n"); + return 0; + } + + seq_printf(m, "%s\n", cmdline); + + /* Zero out data. */ + len = strlen(cmdline); + memset(cmdline, 0, len); + //memblock_free(__pa(cmdline), len + 1); // Should we somehow make this work or just "leak" this memory? + //kmalloc()/kfree() is probably also bad in init/main.c:setup_command_line(). + + return 0; +} + +static int cmdline_hidden_proc_open(struct inode *inode, struct file *file) +{ + /* Only root may open this. */ + if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) { + //if (!uid_eq(current_euid(), GLOBAL_ROOT_UID) || current->pid != 1) { /* We could also restrict access to the init process. */ + return -EACCES; + } + return single_open(file, cmdline_hidden_proc_show, NULL); +} + +static int cmdline_hidden_proc_release(struct inode *inode, struct file *file) +{ + /* Zero out data. */ + struct seq_file *m = file->private_data; + if (m->buf) { + memset(m->buf, 0, m->size); + } + return single_release(inode, file); +} + +static const struct file_operations cmdline_hidden_proc_fops = { + .open = cmdline_hidden_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = cmdline_hidden_proc_release, +}; + static int __init proc_cmdline_init(void) { proc_create("cmdline", 0, NULL, &cmdline_proc_fops); + proc_create("cmdline_hidden", 0, NULL, &cmdline_hidden_proc_fops); return 0; } fs_initcall(proc_cmdline_init); --- linux-orig/include/linux/init.h +++ linux/include/linux/init.h @@ -126,6 +126,7 @@ extern int do_one_initcall(initcall_t fn); extern char __initdata boot_command_line[]; extern char *saved_command_line; +extern char *hidden_command_line; extern unsigned int reset_devices; /* used by init/main.c */ --- linux-orig/init/main.c +++ linux/init/main.c @@ -129,6 +129,8 @@ char __initdata boot_command_line[COMMAND_LINE_SIZE]; /* Untouched saved command line (eg. for /proc) */ char *saved_command_line; +/* Saved hidden command line (for /proc; can be 0) */ +char *hidden_command_line; /* Command line for parameter parsing */ static char *static_command_line; /* Command line for per-initcall parameter parsing */ @@ -369,13 +371,33 @@ */ static void __init setup_command_line(char *command_line) { + size_t len = strlen(boot_command_line); saved_command_line = - memblock_virt_alloc(strlen(boot_command_line) + 1, 0); + memblock_virt_alloc(len + 1, 0); initcall_command_line = - memblock_virt_alloc(strlen(boot_command_line) + 1, 0); + memblock_virt_alloc(len + 1, 0); static_command_line = memblock_virt_alloc(strlen(command_line) + 1, 0); strcpy(saved_command_line, boot_command_line); strcpy(static_command_line, command_line); + + /* Detect hidden command line parameters. + * The format is: Regular parameters, 0, "hdn ", hidden parameters, 0 + */ + if (len < COMMAND_LINE_SIZE - 6 + && boot_command_line[len + 1] == 'h' + && boot_command_line[len + 2] == 'd' + && boot_command_line[len + 3] == 'n' + && boot_command_line[len + 4] == ' ' + ) { + size_t len2; + boot_command_line[COMMAND_LINE_SIZE - 1] = 0; /* Make sure the string is 0 terminated. */ + len2 = strlen(boot_command_line + len + 5); + hidden_command_line = memblock_virt_alloc(len2 + 1, 0); + strcpy(hidden_command_line, boot_command_line + len + 5); + memset(boot_command_line + len + 5, 0, len2); /* Zero out hidden parameters. */ + } else { + hidden_command_line = 0; + } } /*