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