/* "The sco_sock_getsockopt_old function in net/bluetooth/sco.c in the Linux kernel before 2.6.39 does not initialize a certain structure, which allows local users to obtain potentially sensitive information from kernel stack memory via the SCO_CONNINFO option." Fixed e.g. by c4c896e1471aec3b004a693c689f60be3b17ac86 on linux-2.6.39.y in linux-stable. */ /* { dg-do compile } */ /* { dg-options "-fanalyzer" } */ /* { dg-require-effective-target analyzer } */ /* { dg-skip-if "structure layout assumption not met" { default_packed } } */ #include typedef unsigned char __u8; typedef unsigned short __u16; #include "test-uaccess.h" /* Adapted from include/asm-generic/uaccess.h. */ #define get_user(x, ptr) \ ({ \ /* [...snip...] */ \ __get_user_fn(sizeof (*(ptr)), ptr, &(x)); \ /* [...snip...] */ \ }) static inline int __get_user_fn(size_t size, const void __user *ptr, void *x) { size = copy_from_user(x, ptr, size); return size ? -1 : size; } /* Adapted from include/linux/kernel.h. */ #define min_t(type, x, y) ({ \ type __min1 = (x); \ type __min2 = (y); \ __min1 < __min2 ? __min1: __min2; }) /* Adapted from include/linux/net.h. */ struct socket { /* [...snip...] */ struct sock *sk; /* [...snip...] */ }; /* Adapted from include/net/bluetooth/sco.h. */ struct sco_conninfo { __u16 hci_handle; __u8 dev_class[3]; /* { dg-message "padding after field 'dev_class' is uninitialized \\(1 byte\\)" } */ }; struct sco_conn { struct hci_conn *hcon; /* [...snip...] */ }; #define sco_pi(sk) ((struct sco_pinfo *) sk) struct sco_pinfo { /* [...snip...] */ struct sco_conn *conn; }; /* Adapted from include/net/bluetooth/hci_core.h. */ struct hci_conn { /* [...snip...] */ __u16 handle; /* [...snip...] */ __u8 dev_class[3]; /* [...snip...] */ }; /* Adapted from sco_sock_getsockopt_old in net/bluetooth/sco.c. */ static int sco_sock_getsockopt_old_broken(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; /* [...snip...] */ struct sco_conninfo cinfo; /* { dg-message "region created on stack here" "where" } */ /* { dg-message "capacity: 6 bytes" "capacity" { target *-*-* } .-1 } */ /* Note: 40 bits of fields, padded to 48. */ int len, err = 0; /* [...snip...] */ if (get_user(len, optlen)) return -1; /* [...snip...] */ /* case SCO_CONNINFO: */ cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *)&cinfo, len)) /* { dg-warning "potential exposure of sensitive information by copying uninitialized data from stack" "warning" { target *-*-* } } */ /* { dg-message "1 byte is uninitialized" "how much note" { target *-*-* } .-1 } */ err = -1; /* [...snip...] */ } static int sco_sock_getsockopt_fixed(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; /* [...snip...] */ struct sco_conninfo cinfo; /* Note: 40 bits of fields, padded to 48. */ int len, err = 0; /* [...snip...] */ if (get_user(len, optlen)) return -1; /* [...snip...] */ /* case SCO_CONNINFO: */ /* Infoleak fixed by this memset call. */ memset(&cinfo, 0, sizeof(cinfo)); cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *)&cinfo, len)) /* { dg-bogus "exposure" } */ err = -1; /* [...snip...] */ }