Linux Kernel unsigned long p = *ppos integer truncation bug

In the Linux Kernel, the file pointer is of type long long.  This equates to an integer with a width of 64bits on most or all platforms (its architecture specific).

 Its a bug that has occured numerous times before, and occasionaly is exploitable when the 64bit file pointer is truncated to 32 bits.

 An interesting, but non exploitable bug is in fs/proc/base.c.


static ssize_t mem_read(struct file * file, char __user * buf,
  size_t count, loff_t *ppos)
 {
  struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
  char *page;

The next line contains the bug. ppos is long long, which becomes truncated.

  unsigned long src = *ppos;
...
  while (count > 0) {
  int this_len, retval;
  this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
  retval = access_process_vm(task, src, page, this_len, 0);
  if (!retval || !MAY_PTRACE(task) || !ptrace_may_attach(task)) {
  if (!ret)
  ret = -EIO;
  break;
  }
  if (copy_to_user(buf, page, retval)) {
  ret = -EFAULT;
  break;
  }
  ret += retval;
  src += retval;
  buf += retval;
  count -= retval;

The interesting thing about this bug, is the ‘undefined’ behaviour when we seek to UINT_MAX.  In effect, its the same as if we were at file position 0.

Now this is quite a pointless bug from an auditors perspective, but it must be stated that every time we see in the kernel, file pointer truncation, the code must be examined to verify that it is not exploitable. This bug represents a class of bug that may be exploitable under certain conditions and always warrants inspection.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s