1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2011 Google, Inc.
4  */
5 
6 #include <linux/kernel.h>
7 #include <linux/file.h>
8 #include <linux/fs.h>
9 #include <linux/uaccess.h>
10 
11 #include "ion.h"
12 
13 union ion_ioctl_arg {
14 	struct ion_allocation_data allocation;
15 	struct ion_heap_query query;
16 };
17 
validate_ioctl_arg(unsigned int cmd,union ion_ioctl_arg * arg)18 static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
19 {
20 	switch (cmd) {
21 	case ION_IOC_HEAP_QUERY:
22 		if (arg->query.reserved0 ||
23 		    arg->query.reserved1 ||
24 		    arg->query.reserved2)
25 			return -EINVAL;
26 		break;
27 	default:
28 		break;
29 	}
30 
31 	return 0;
32 }
33 
34 /* fix up the cases where the ioctl direction bits are incorrect */
ion_ioctl_dir(unsigned int cmd)35 static unsigned int ion_ioctl_dir(unsigned int cmd)
36 {
37 	switch (cmd) {
38 	default:
39 		return _IOC_DIR(cmd);
40 	}
41 }
42 
ion_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)43 long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
44 {
45 	int ret = 0;
46 	unsigned int dir;
47 	union ion_ioctl_arg data;
48 
49 	dir = ion_ioctl_dir(cmd);
50 
51 	if (_IOC_SIZE(cmd) > sizeof(data))
52 		return -EINVAL;
53 
54 	/*
55 	 * The copy_from_user is unconditional here for both read and write
56 	 * to do the validate. If there is no write for the ioctl, the
57 	 * buffer is cleared
58 	 */
59 	if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
60 		return -EFAULT;
61 
62 	ret = validate_ioctl_arg(cmd, &data);
63 	if (ret) {
64 		pr_warn_once("%s: ioctl validate failed\n", __func__);
65 		return ret;
66 	}
67 
68 	if (!(dir & _IOC_WRITE))
69 		memset(&data, 0, sizeof(data));
70 
71 	switch (cmd) {
72 	case ION_IOC_ALLOC:
73 	{
74 		int fd;
75 
76 		fd = ion_alloc(data.allocation.len,
77 			       data.allocation.heap_id_mask,
78 			       data.allocation.flags);
79 		if (fd < 0)
80 			return fd;
81 
82 		data.allocation.fd = fd;
83 
84 		break;
85 	}
86 	case ION_IOC_HEAP_QUERY:
87 		ret = ion_query_heaps(&data.query);
88 		break;
89 	default:
90 		return -ENOTTY;
91 	}
92 
93 	if (dir & _IOC_READ) {
94 		if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd)))
95 			return -EFAULT;
96 	}
97 	return ret;
98 }
99