Skip to contentSkip to conversation

xen/privcmd: prevent integer overflow on 32 bit systems

PATCH In review Dan Carpenter 3 participants 4 messages Jul 15, 2022 – Jul 19, 2022

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 xen/privcmd: prevent integer overflow on 32 bit systems

The "m.num * sizeof(*m.arr)" multiplication can have an integer overflow on 32 bit systems. Probably no one really uses this software on 32 bit systems, but it's still worth fixing the bug if only to make the static checker happy.

Fixes ceb90fa0a800 ("xen/privcmd: add PRIVCMD_MMAPBATCH_V2 ioctl")
---
 drivers/xen/privcmd.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/xen/privcmd.c +4 -0
index ad17166b0ef6..1e59b76c618e 100644
@@ -456,6 +456,8 @@ static long privcmd_ioctl_mmap_batch(
 		if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch)))
 			return -EFAULT;
 		/* Returns per-frame error in m.arr. */
+		if (m.num > SIZE_MAX / sizeof(*m.arr))
+			return -EINVAL;
 		m.err = NULL;
 		if (!access_ok(m.arr, m.num * sizeof(*m.arr)))
 			return -EFAULT;
@@ -464,6 +466,8 @@ static long privcmd_ioctl_mmap_batch(
 		if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2)))
 			return -EFAULT;
 		/* Returns per-frame error code in m.err. */
+		if (m.num > SIZE_MAX / sizeof(*m.arr))
+			return -EINVAL;
 		if (!access_ok(m.err, m.num * (sizeof(*m.err))))
 			return -EFAULT;
 		break;
Signature · 1 line
2.35.1
#1 lore raw

On 15.07.22 11:20, Dan Carpenter wrote:

Hello Dan

Quoted 25 lines

The "m.num * sizeof(*m.arr)" multiplication can have an integer overflow on 32 bit systems. Probably no one really uses this software on 32 bit systems, but it's still worth fixing the bug if only to make the static checker happy.

Fixes ceb90fa0a800 ("xen/privcmd: add PRIVCMD_MMAPBATCH_V2 ioctl")
---
  drivers/xen/privcmd.c | 4 ++++
  1 file changed, 4 insertions(+)
drivers/xen/privcmd.c +2 -0
index ad17166b0ef6..1e59b76c618e 100644
@@ -456,6 +456,8 @@ static long privcmd_ioctl_mmap_batch(
  		if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch)))
  			return -EFAULT;
  		/* Returns per-frame error in m.arr. */
+		if (m.num > SIZE_MAX / sizeof(*m.arr))
+			return -EINVAL;
  		m.err = NULL;
  		if (!access_ok(m.arr, m.num * sizeof(*m.arr)))
  			return -EFAULT;

drivers/xen/privcmd.c static long privcmd_ioctl_mmap_batch( · line 469

Quoted 4 lines
drivers/xen/privcmd.c +1 -0
@@ -464,6 +466,8 @@ static long privcmd_ioctl_mmap_batch(
  		if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2)))
  			return -EFAULT;
  		/* Returns per-frame error code in m.err. */
+		if (m.num > SIZE_MAX / sizeof(*m.arr))

Looks like here we need to check against sizeof(*m.err) which is used in the multiplication below.

Quoted 4 lines
drivers/xen/privcmd.c +1 -0
+			return -EINVAL;
  		if (!access_ok(m.err, m.num * (sizeof(*m.err))))
  			return -EFAULT;
  		break;
Signature · 3 lines
Regards,

Oleksandr Tyshchenko
#2 lore raw
Quoted 30 lines · Oleksandr Tyshchenko

On 15.07.22 11:20, Dan Carpenter wrote:

Hello Dan

Quoted 24 lines

The "m.num * sizeof(*m.arr)" multiplication can have an integer overflow on 32 bit systems. Probably no one really uses this software on 32 bit systems, but it's still worth fixing the bug if only to make the static checker happy.

Fixes ceb90fa0a800 ("xen/privcmd: add PRIVCMD_MMAPBATCH_V2 ioctl")
---
  drivers/xen/privcmd.c | 4 ++++
  1 file changed, 4 insertions(+)
drivers/xen/privcmd.c +2 -0
index ad17166b0ef6..1e59b76c618e 100644
@@ -456,6 +456,8 @@ static long privcmd_ioctl_mmap_batch(
  		if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch)))
  			return -EFAULT;
  		/* Returns per-frame error in m.arr. */
+		if (m.num > SIZE_MAX / sizeof(*m.arr))
+			return -EINVAL;
  		m.err = NULL;
  		if (!access_ok(m.arr, m.num * sizeof(*m.arr)))
  			return -EFAULT;
Quoted 8 lines

drivers/xen/privcmd.c static long privcmd_ioctl_mmap_batch( · line 469

Quoted 5 lines
drivers/xen/privcmd.c +1 -0
@@ -464,6 +466,8 @@ static long privcmd_ioctl_mmap_batch(
  		if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2)))
  			return -EFAULT;
  		/* Returns per-frame error code in m.err. */
+		if (m.num > SIZE_MAX / sizeof(*m.arr))

Looks like here we need to check against sizeof(*m.err) which is used in the multiplication below.

Oh, yeah. Sorry! Need to redo that.

regards,
dan carpenter

#3 lore raw
In reply to Dan Carpenter
Thank you for the patch! Perhaps something to improve:

Hi Dan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on xen-tip/linux-next]
[also build test WARNING on linus/master v5.19-rc7 next-20220719] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Dan-Carpenter/xen-privcmd-prevent-integer-overflow-on-32-bit-systems/20220715-162307
base:   https://git.kernel.org/pub/scm/linux/kernel/git/xen/tip.git linux-next
config: x86_64-randconfig-a005 (https://download.01.org/0day-ci/archive/20220720/[email protected]/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project fa0c7639e91fa1cd0cf2ff0445a1634a90fe850a)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/ea22ebd83753c7181043e69251b78f0be73675ad
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Dan-Carpenter/xen-privcmd-prevent-integer-overflow-on-32-bit-systems/20220715-162307
git checkout ea22ebd83753c7181043e69251b78f0be73675ad
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/acpi/ drivers/ata/ drivers/rtc/ drivers/thermal/intel/ drivers/xen/

If you fix the issue, kindly add following tag where applicable

All warnings (new ones prefixed by >>):

>> drivers/xen/privcmd.c:459:13: warning: result of comparison of constant 2305843009213693951 with expression of type 'unsigned int' is always false [-Wtautological-constant-out-of-range-compare]
                   if (m.num > SIZE_MAX / sizeof(*m.arr))
                       ~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/xen/privcmd.c:469:13: warning: result of comparison of constant 2305843009213693951 with expression of type 'unsigned int' is always false [-Wtautological-constant-out-of-range-compare]
                   if (m.num > SIZE_MAX / sizeof(*m.arr))
                       ~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~

2 warnings generated.

vim +459 drivers/xen/privcmd.c

441
442 static long privcmd_ioctl_mmap_batch(
443 struct file *file, void __user *udata, int version)
444 {
445 struct privcmd_data *data = file->private_data;
446 int ret;
447 struct privcmd_mmapbatch_v2 m;
448 struct mm_struct *mm = current->mm;
449 struct vm_area_struct *vma;
450 unsigned long nr_pages;
451 LIST_HEAD(pagelist);
452 struct mmap_batch_state state;
453
454 switch (version) {
455 case 1:
456 if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch)))
457 return -EFAULT;
458 /* Returns per-frame error in m.arr. */

 > 459			if (m.num > SIZE_MAX / sizeof(*m.arr))
   460				return -EINVAL;
   461			m.err = NULL;
   462			if (!access_ok(m.arr, m.num * sizeof(*m.arr)))
   463				return -EFAULT;
   464			break;
   465		case 2:
   466			if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2)))
   467				return -EFAULT;
   468			/* Returns per-frame error code in m.err. */
   469			if (m.num > SIZE_MAX / sizeof(*m.arr))
   470				return -EINVAL;
   471			if (!access_ok(m.err, m.num * (sizeof(*m.err))))
   472				return -EFAULT;
   473			break;
   474		default:
   475			return -EINVAL;
   476		}
   477	
   478		/* If restriction is in place, check the domid matches */
   479		if (data->domid != DOMID_INVALID && data->domid != m.dom)
   480			return -EPERM;
   481	
   482		nr_pages = DIV_ROUND_UP(m.num, XEN_PFN_PER_PAGE);
   483		if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT)))
   484			return -EINVAL;
   485	
   486		ret = gather_array(&pagelist, m.num, sizeof(xen_pfn_t), m.arr);
   487	
   488		if (ret)
   489			goto out;
   490		if (list_empty(&pagelist)) {
   491			ret = -EINVAL;
   492			goto out;
   493		}
   494	
   495		if (version == 2) {
   496			/* Zero error array now to only copy back actual errors. */
   497			if (clear_user(m.err, sizeof(int) * m.num)) {
   498				ret = -EFAULT;
   499				goto out;
   500			}
   501		}
   502	
   503		mmap_write_lock(mm);
   504	
   505		vma = find_vma(mm, m.addr);
   506		if (!vma ||
   507		    vma->vm_ops != &privcmd_vm_ops) {
   508			ret = -EINVAL;
   509			goto out_unlock;
   510		}
   511	
   512		/*
   513		 * Caller must either:
   514		 *
   515		 * Map the whole VMA range, which will also allocate all the
   516		 * pages required for the auto_translated_physmap case.
   517		 *
   518		 * Or
   519		 *
   520		 * Map unmapped holes left from a previous map attempt (e.g.,
   521		 * because those foreign frames were previously paged out).
   522		 */
   523		if (vma->vm_private_data == NULL) {
   524			if (m.addr != vma->vm_start ||
   525			    m.addr + (nr_pages << PAGE_SHIFT) != vma->vm_end) {
   526				ret = -EINVAL;
   527				goto out_unlock;
   528			}
   529			if (xen_feature(XENFEAT_auto_translated_physmap)) {
   530				ret = alloc_empty_pages(vma, nr_pages);
   531				if (ret < 0)
   532					goto out_unlock;
   533			} else
   534				vma->vm_private_data = PRIV_VMA_LOCKED;
   535		} else {
   536			if (m.addr < vma->vm_start ||
   537			    m.addr + (nr_pages << PAGE_SHIFT) > vma->vm_end) {
   538				ret = -EINVAL;
   539				goto out_unlock;
   540			}
   541			if (privcmd_vma_range_is_mapped(vma, m.addr, nr_pages)) {
   542				ret = -EINVAL;
   543				goto out_unlock;
   544			}
   545		}
   546	
   547		state.domain        = m.dom;
   548		state.vma           = vma;
   549		state.va            = m.addr;
   550		state.index         = 0;
   551		state.global_error  = 0;
   552		state.version       = version;

553
554 BUILD_BUG_ON(((PAGE_SIZE / sizeof(xen_pfn_t)) % XEN_PFN_PER_PAGE) != 0);
555 /* mmap_batch_fn guarantees ret == 0 */
556 BUG_ON(traverse_pages_block(m.num, sizeof(xen_pfn_t),
557 &pagelist, mmap_batch_fn, &state));
558
559 mmap_write_unlock(mm);
560
561 if (state.global_error) {
562 /* Write back errors in second pass. */
563 state.user_gfn = (xen_pfn_t *)m.arr;
564 state.user_err = m.err;
565 ret = traverse_pages_block(m.num, sizeof(xen_pfn_t),
566 &pagelist, mmap_return_errors, &state);
567 } else
568 ret = 0;
569
570 /* If we have not had any EFAULT-like global errors then set the global
571 * error to -ENOENT if necessary. */
572 if ((ret == 0) && (state.global_error == -ENOENT))
573 ret = -ENOENT;
574
575 out:
576 free_page_list(&pagelist);
577 return ret;
578
579 out_unlock:
580 mmap_write_unlock(mm);
581 goto out;
582 }
583

Signature · 2 lines
0-DAY CI Kernel Test Service
https://01.org/lkp