Linux Kernel Infoleaks

Here are 6 Linux kernel local infoleaks.

InfoSect is available for engagements in code review. Please look at or check out some of our public code review videos on Or, check out our code review training at

1 ==========================


struct ipddp_route
        struct net_device *dev;             /* Carrier device */
        __be32 ip;                       /* IP address */
        struct atalk_addr at;              /* Gateway appletalk address */
        int flags;
        struct ipddp_route *next;


static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
        struct ipddp_route *f;

        for(f = ipddp_route_list; f != NULL; f = f->next)
                if(f->ip == rt->ip &&
                   f->at.s_net == rt->at.s_net &&
                   f->at.s_node == rt->at.s_node)
                        return f;

        return NULL;

                case SIOCFINDIPDDPRT:
                        rp = __ipddp_find_route(&rcp);
                        if (rp)
                                memcpy(&rcp2, rp, sizeof(rcp2));

                        if (rp) {
                                if (copy_to_user(rt, &rcp2,
                                                 sizeof(struct ipddp_route)))
                                        return -EFAULT;
                                return 0;
                        } else
                                return -ENOENT;

++ in the ipddp_route struct, there are pointers next and dev which get
++ leaked here.

2 ===================================================

struct snd_ctl_elem_list {
        unsigned int offset;            /* W: first element ID to get */
        unsigned int space;             /* W: count of element IDs to get */
        unsigned int used;              /* R: count of element IDs set */
        unsigned int count;             /* R: count of all elements */
        struct snd_ctl_elem_id __user *pids; /* R: IDs */
        unsigned char reserved[50];
+++ note reserved, pids


static int snd_ctl_elem_list(struct snd_card *card,
                             struct snd_ctl_elem_list __user *_list)
        struct snd_ctl_elem_list list;
        struct snd_kcontrol *kctl;
        struct snd_ctl_elem_id id;
        unsigned int offset, space, jidx;
        int err = 0;

               list_for_each_entry(kctl, &card->controls, list) {
                        if (offset >= kctl->count) {
                                offset -= kctl->count;
                        for (jidx = offset; jidx < kctl->count; jidx++) {
                                snd_ctl_build_ioff(&id, kctl, jidx);
                                if (copy_to_user(list.pids + list.used, &id,
                                                 sizeof(id))) {
                                        err = -EFAULT;
                                        goto out;
                                if (!--space)
                                        goto out;
                        offset = 0;
        if (!err && copy_to_user(_list, &list, sizeof(list)))
                err = -EFAULT;
+++ copying list back to userspace leaks a pointer at least

static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
        struct snd_ctl_file *ctl;
        struct snd_card *card;
        struct snd_kctl_ioctl *p;
        void __user *argp = (void __user *)arg;
        int __user *ip = argp;
        int err;

        ctl = file->private_data;
        card = ctl->card;
        if (snd_BUG_ON(!card))
                return -ENXIO;
        switch (cmd) {
                return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
                return snd_ctl_card_info(card, ctl, cmd, argp);
                return snd_ctl_elem_list(card, argp);

3 ======================================

struct snd_emu10k1_fx8010_info {
        unsigned int internal_tram_size;        /* in samples */
        unsigned int external_tram_size;        /* in samples */
        char fxbus_names[16][32];               /* names of FXBUSes */
        char extin_names[16][32];               /* names of external inputs */
        char extout_names[32][32];              /* names of external outputs */
        unsigned int gpr_controls;              /* count of GPR controls */
+++ note the strings/names are fixed lengths of 32


static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg)
        switch (cmd) {
                emu->support_tlv = 1;
                return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp);
        case SNDRV_EMU10K1_IOCTL_INFO:
                info = kmalloc(sizeof(*info), GFP_KERNEL);
+++ note kmalloc
                if (!info)
                        return -ENOMEM;
                snd_emu10k1_fx8010_info(emu, info);
                if (copy_to_user(argp, info, sizeof(*info))) {
                        return -EFAULT;
                return 0;


static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
                                   struct snd_emu10k1_fx8010_info *info)
        char **fxbus, **extin, **extout;
        unsigned short fxbus_mask, extin_mask, extout_mask;
        int res;

        info->internal_tram_size = emu->fx8010.itram_size;
        info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
        fxbus = fxbuses;
        extin = emu->audigy ? audigy_ins : creative_ins;
        extout = emu->audigy ? audigy_outs : creative_outs;
        fxbus_mask = emu->fx8010.fxbus_mask;
        extin_mask = emu->fx8010.extin_mask;
        extout_mask = emu->fx8010.extout_mask;
        for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
                copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
                copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
                copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+++ info leaks. the copy_strings partially fill the structure/strings which
+++ are 32 bytes. the remainder of the string is uninitialized (from kmalloc)

static void copy_string(char *dst, char *src, char *null, int idx)
        if (src == NULL)
                sprintf(dst, "%s %02X", null, idx);
                strcpy(dst, src);

4 ====================================

struct inquiry_data {
        bdaddr_t        bdaddr;
        __u8            pscan_rep_mode;
        __u8            pscan_period_mode;
        __u8            pscan_mode;
        __u8            dev_class[3];
        __le16          clock_offset;
        __s8            rssi;
        __u8            ssp_mode;


static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
        struct discovery_state *cache = &hdev->discovery;
        struct inquiry_info *info = (struct inquiry_info *) buf;
        struct inquiry_entry *e;
        int copied = 0;

        list_for_each_entry(e, &cache->all, all) {
                struct inquiry_data *data = &e->data;

                if (copied >= num)

                bacpy(&info->bdaddr, &data->bdaddr);
                info->pscan_rep_mode    = data->pscan_rep_mode;
                info->pscan_period_mode = data->pscan_period_mode;
                info->pscan_mode        = data->pscan_mode;
                memcpy(info->dev_class, data->dev_class, 3);
                info->clock_offset      = data->clock_offset;
+++ note rssi and ssp_mode fields are not filled
+++ because this is from a kmalloc, it is left uninitialized. hence an infoleak


       /* cache_dump can't sleep. Therefore we allocate temp buffer and then
         * copy it to the user space.
        buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL);
+++ note kmalloc
        if (!buf) {
                err = -ENOMEM;
                goto done;

        ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);

        BT_DBG("num_rsp %d", ir.num_rsp);

        if (!copy_to_user(ptr, &ir, sizeof(ir))) {
                ptr += sizeof(ir);
                if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) *
                        err = -EFAULT;
        } else
                err = -EFAULT;


5 ========================================================


        buf = vmalloc(mr->buffer_size);
        if (!buf) {
                DBG("vmalloc failed\n");
                return -ENOMEM;

        r = display->driver->memory_read(display, buf, mr->buffer_size,
                        mr->x, mr->y, mr->w, mr->h);
+++ r returns the amount of memory read. can be less than buffer_size

        if (r > 0) {
                if (copy_to_user(mr->buffer, buf, mr->buffer_size))
+++ copies the entire buffer
+++ hence, an infoleak from unitialised memory from vmalloc
                       r = -EFAULT;


6 ==============================================

static long sn_hwperf_ioctl(struct file *fp, u32 op, unsigned long arg)
        struct sn_hwperf_ioctl_args a;
        r = copy_from_user(&a, (const void __user *)arg,
                sizeof(struct sn_hwperf_ioctl_args));
        if (r != 0) {
                r = -EFAULT;
                goto error;
        if (a.ptr) {
                p = vmalloc(;
                if (!p) {
                        r = -ENOMEM;
                        goto error;

        if (op & SN_HWPERF_OP_MEM_COPYIN) {
                r = copy_from_user(p, (const void __user *)a.ptr,;
                if (r != 0) {
                        r = -EFAULT;
                        goto error;


        switch (op) {
        case SN_HWPERF_GET_CPU_INFO:
        case SN_HWPERF_GET_MMRS:
        case SN_HWPERF_SET_MMRS:
                op_info.p = p;
                op_info.a = &a;
                op_info.v0 = &v0;
                op_info.op = op;
                r = sn_hwperf_op_cpu(&op_info);
                if (r) {
                        r = sn_hwperf_map_err(r);
                        a.v0 = v0;
                        goto error;

                /* all other ops are a direct SAL call */
                r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op,
                              a.arg,, (u64) p, 0, 0, &v0);
                if (r) {
                        r = sn_hwperf_map_err(r);
                        goto error;
                a.v0 = v0;
+++ if any of the above cases don't fully overwrite the memory buffer from
+++ vmalloc, then we might have uninitialized memory and hence an infoleak

        if (op & SN_HWPERF_OP_MEM_COPYOUT) {
                r = copy_to_user((void __user *)a.ptr, p,;
                if (r != 0) {
                        r = -EFAULT;
                        goto error;



  1. You may see the ingredients list, but you won’t know which one goes in what percentage and other details. These formulations have been tested for their quality and efficacy. Low-quality products won’t give you such results. We hope this article cleared all your doubts about a hair follicle drug test and the various detox methods. Incidentally, most detox programs are designed for urine tests. Detox drinks can even work at the eleventh hour if you follow the instructions correctly. But there’s hardly anything you can do overnight to pass a hair follicle test. Once the THC metabolites and other toxins get embedded in your hair follicle, it’s tough to get rid of those. Then, you would have no option but to wait for your hair to grow out. But the hair shampoo should work to some extent if you are a light user. To make the whole process more convenient and less suspicious, many of these fake urine kits come equipped with elastic belts for holding the cup and heat pads to maintain the required temperature. Do not be intimidated by the whole process, because along with your products, you will receive an instruction manual too. These kits are 100% foolproof and reasonably easy to use. To introduce you to this relatively unexplored world of synthetic urine kits, we have come up with the top five that can make your life with cannabis much easier. It is usually quite effective. Most people have found it to be so.


Post a Comment

Popular posts from this blog

C++ Memory Corruption (std::vector) - part 2

Pointer Compression in V8

Linux Kernel Stack Smashing