1 /*
2  * SPDX-License-Identifier: MIT
3  *
4  * Copyright © 2018 Intel Corporation
5  */
6 
7 #include <linux/nospec.h>
8 #include <linux/sched/signal.h>
9 #include <linux/uaccess.h>
10 
11 #include <uapi/drm/i915_drm.h>
12 
13 #include "i915_user_extensions.h"
14 #include "i915_utils.h"
15 
i915_user_extensions(struct i915_user_extension __user * ext,const i915_user_extension_fn * tbl,unsigned int count,void * data)16 int i915_user_extensions(struct i915_user_extension __user *ext,
17 			 const i915_user_extension_fn *tbl,
18 			 unsigned int count,
19 			 void *data)
20 {
21 	unsigned int stackdepth = 512;
22 
23 	while (ext) {
24 		int i, err;
25 		u32 name;
26 		u64 next;
27 
28 		if (!stackdepth--) /* recursion vs useful flexibility */
29 			return -E2BIG;
30 
31 		err = check_user_mbz(&ext->flags);
32 		if (err)
33 			return err;
34 
35 		for (i = 0; i < ARRAY_SIZE(ext->rsvd); i++) {
36 			err = check_user_mbz(&ext->rsvd[i]);
37 			if (err)
38 				return err;
39 		}
40 
41 		if (get_user(name, &ext->name))
42 			return -EFAULT;
43 
44 		err = -EINVAL;
45 		if (name < count) {
46 			name = array_index_nospec(name, count);
47 			if (tbl[name])
48 				err = tbl[name](ext, data);
49 		}
50 		if (err)
51 			return err;
52 
53 		if (get_user(next, &ext->next_extension) ||
54 		    overflows_type(next, uintptr_t))
55 			return -EFAULT;
56 
57 		ext = u64_to_user_ptr(next);
58 	}
59 
60 	return 0;
61 }
62