linux
"Dereferencing uninitialized pointer causes Kernel crash
During the initialization of a UNIX98 pseudo-terminal by ptmx_open, a tty_struct structure is allocated. But before its pointer field link->driver_data is properly initialized, ptmx_open will try to allocate an inode structure for the PTY slave. If this allocation fails, some cleanup code must be executed to free the already allocated resources. Namely, pty_close will be called to release the previously opened tty, and this eventually dereferences tty->link->driver_data, which is assumed to have been already initialized."
Bug fixed by commit 7acf6cd80b2
Type | UninitializedVariable |
Config | "UNIX98_PTYS && DEVPTS_MULTIPLE_INSTANCES" (2nd degree) |
C-features | FunctionPointers |
Fix-in | code |
Location | drivers/tty/ |
__attribute__ ((noinline)) int nondet() { return 42; } int some_int = 1; void pts_sb_from_inode(int *inode) { #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES int x = *inode; // ERROR (7) #endif } void devpts_pty_kill(int *inode) { pts_sb_from_inode(inode); // (6) } void pty_close(int *driver_data) { #ifdef CONFIG_UNIX98_PTYS devpts_pty_kill(driver_data); // (5) #endif } int tty_release(int *driver_data) { pty_close(driver_data); // (4) return 0; } #ifdef CONFIG_UNIX98_PTYS int ptmx_open() { int *driver_data; if (nondet()) { goto err_release; // (2) } driver_data = &some_int; return 0; err_release: tty_release(driver_data); // (3) return -1; } #endif int main() { #ifdef CONFIG_UNIX98_PTYS ptmx_open(); // (1) #endif return 0; }
diff --git a/simple/7acf6cd.c b/simple/7acf6cd.c --- a/simple/7acf6cd.c +++ b/simple/7acf6cd.c @@ -18,7 +18,8 @@ void pty_close(int *driver_data) { #ifdef CONFIG_UNIX98_PTYS - devpts_pty_kill(driver_data); // (5) + if(driver_data) + devpts_pty_kill(driver_data); // (5) #endif } @@ -32,12 +33,12 @@ int ptmx_open() { int *driver_data; + + driver_data = &some_int; if (nondet()) { goto err_release; // (2) } - - driver_data = &some_int; return 0;
#ifdef UNK_TRUE #define unk() 1 #else #include <stdlib.h> #define unk() (rand() % 2) #endif int some_int = 1; int main() { #ifdef CONFIG_UNIX98_PTYS // ptmx_open(); int *driver_data; if (unk()) { goto err_release; } driver_data = &some_int; return 0; err_release: #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES if (*driver_data) ; #endif return -1; #endif }
. // setup . call drivers/tty/pty.c:807:pty_init() .. call drivers/tty/pty.c:739:unix98_pty_init() .. 785: tty_set_operations(pts_driver, &pty_unix98_ops); . call drivers/tty/pty.c:669:ptmx_open() . 671: struct tty_struct *tty; . // 694: tty = tty_init_dev(ptm_driver, index); . // will not set tty->link->driver_data . 707: tty_add_file(tty, filp); . // now filp->private_data->tty points to tty . 709: slave_inode = devpts_pty_new(inode, ..., tty->link); . // now inode->i_private points to tty->link . 712: if (IS_ERR(slave_inode)) { . 714: goto err_release; . // tty->link->driver_data is not set, remains undefined . 727: tty_release(inode, filp); .. call drivers/tty/tty_io.c:1625:tty_release() .. 1627: struct tty_struct *tty = file_tty(filp); .. // tty_release.tty points to ptmx_open.tty .. 1658: tty->ops->close(tty, filp); ... dyn-call drivers/tty/pty.c:35:pty_close() ... 58: devpts_pty_kill(tty->link->driver_data); .... call fs/devpts/inode.c:629:devpts_pty_kill() .... // inode points to ptmx_open.tty->link->driver_data .... 631: struct super_block *sb = pts_sb_from_inode(inode); ..... call fs/devpts/inode.c:137:pts_sb_from_inode() ..... ERROR 140: if (inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) ..... // tty->link->driver_data, holding an undefined value, is dereferenced