1 // SPDX-License-Identifier: GPL-2.0 1 // SPDX-License-Identifier: GPL-2.0 2 /* 2 /* 3 * device_cgroup.c - device cgroup subsystem 3 * device_cgroup.c - device cgroup subsystem 4 * 4 * 5 * Copyright 2007 IBM Corp 5 * Copyright 2007 IBM Corp 6 */ 6 */ 7 7 8 #include <linux/bpf-cgroup.h> 8 #include <linux/bpf-cgroup.h> 9 #include <linux/device_cgroup.h> 9 #include <linux/device_cgroup.h> 10 #include <linux/cgroup.h> 10 #include <linux/cgroup.h> 11 #include <linux/ctype.h> 11 #include <linux/ctype.h> 12 #include <linux/list.h> 12 #include <linux/list.h> 13 #include <linux/uaccess.h> 13 #include <linux/uaccess.h> 14 #include <linux/seq_file.h> 14 #include <linux/seq_file.h> 15 #include <linux/slab.h> 15 #include <linux/slab.h> 16 #include <linux/rcupdate.h> 16 #include <linux/rcupdate.h> 17 #include <linux/mutex.h> 17 #include <linux/mutex.h> 18 18 19 #ifdef CONFIG_CGROUP_DEVICE 19 #ifdef CONFIG_CGROUP_DEVICE 20 20 21 static DEFINE_MUTEX(devcgroup_mutex); 21 static DEFINE_MUTEX(devcgroup_mutex); 22 22 23 enum devcg_behavior { 23 enum devcg_behavior { 24 DEVCG_DEFAULT_NONE, 24 DEVCG_DEFAULT_NONE, 25 DEVCG_DEFAULT_ALLOW, 25 DEVCG_DEFAULT_ALLOW, 26 DEVCG_DEFAULT_DENY, 26 DEVCG_DEFAULT_DENY, 27 }; 27 }; 28 28 29 /* 29 /* 30 * exception list locking rules: 30 * exception list locking rules: 31 * hold devcgroup_mutex for update/read. 31 * hold devcgroup_mutex for update/read. 32 * hold rcu_read_lock() for read. 32 * hold rcu_read_lock() for read. 33 */ 33 */ 34 34 35 struct dev_exception_item { 35 struct dev_exception_item { 36 u32 major, minor; 36 u32 major, minor; 37 short type; 37 short type; 38 short access; 38 short access; 39 struct list_head list; 39 struct list_head list; 40 struct rcu_head rcu; 40 struct rcu_head rcu; 41 }; 41 }; 42 42 43 struct dev_cgroup { 43 struct dev_cgroup { 44 struct cgroup_subsys_state css; 44 struct cgroup_subsys_state css; 45 struct list_head exceptions; 45 struct list_head exceptions; 46 enum devcg_behavior behavior; 46 enum devcg_behavior behavior; 47 }; 47 }; 48 48 49 static inline struct dev_cgroup *css_to_devcgr 49 static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) 50 { 50 { 51 return s ? container_of(s, struct dev_ 51 return s ? container_of(s, struct dev_cgroup, css) : NULL; 52 } 52 } 53 53 54 static inline struct dev_cgroup *task_devcgrou 54 static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) 55 { 55 { 56 return css_to_devcgroup(task_css(task, 56 return css_to_devcgroup(task_css(task, devices_cgrp_id)); 57 } 57 } 58 58 59 /* 59 /* 60 * called under devcgroup_mutex 60 * called under devcgroup_mutex 61 */ 61 */ 62 static int dev_exceptions_copy(struct list_hea 62 static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig) 63 { 63 { 64 struct dev_exception_item *ex, *tmp, * 64 struct dev_exception_item *ex, *tmp, *new; 65 65 66 lockdep_assert_held(&devcgroup_mutex); 66 lockdep_assert_held(&devcgroup_mutex); 67 67 68 list_for_each_entry(ex, orig, list) { 68 list_for_each_entry(ex, orig, list) { 69 new = kmemdup(ex, sizeof(*ex), 69 new = kmemdup(ex, sizeof(*ex), GFP_KERNEL); 70 if (!new) 70 if (!new) 71 goto free_and_exit; 71 goto free_and_exit; 72 list_add_tail(&new->list, dest 72 list_add_tail(&new->list, dest); 73 } 73 } 74 74 75 return 0; 75 return 0; 76 76 77 free_and_exit: 77 free_and_exit: 78 list_for_each_entry_safe(ex, tmp, dest 78 list_for_each_entry_safe(ex, tmp, dest, list) { 79 list_del(&ex->list); 79 list_del(&ex->list); 80 kfree(ex); 80 kfree(ex); 81 } 81 } 82 return -ENOMEM; 82 return -ENOMEM; 83 } 83 } 84 84 85 static void dev_exceptions_move(struct list_he 85 static void dev_exceptions_move(struct list_head *dest, struct list_head *orig) 86 { 86 { 87 struct dev_exception_item *ex, *tmp; 87 struct dev_exception_item *ex, *tmp; 88 88 89 lockdep_assert_held(&devcgroup_mutex); 89 lockdep_assert_held(&devcgroup_mutex); 90 90 91 list_for_each_entry_safe(ex, tmp, orig 91 list_for_each_entry_safe(ex, tmp, orig, list) { 92 list_move_tail(&ex->list, dest 92 list_move_tail(&ex->list, dest); 93 } 93 } 94 } 94 } 95 95 96 /* 96 /* 97 * called under devcgroup_mutex 97 * called under devcgroup_mutex 98 */ 98 */ 99 static int dev_exception_add(struct dev_cgroup 99 static int dev_exception_add(struct dev_cgroup *dev_cgroup, 100 struct dev_except 100 struct dev_exception_item *ex) 101 { 101 { 102 struct dev_exception_item *excopy, *wa 102 struct dev_exception_item *excopy, *walk; 103 103 104 lockdep_assert_held(&devcgroup_mutex); 104 lockdep_assert_held(&devcgroup_mutex); 105 105 106 excopy = kmemdup(ex, sizeof(*ex), GFP_ 106 excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL); 107 if (!excopy) 107 if (!excopy) 108 return -ENOMEM; 108 return -ENOMEM; 109 109 110 list_for_each_entry(walk, &dev_cgroup- 110 list_for_each_entry(walk, &dev_cgroup->exceptions, list) { 111 if (walk->type != ex->type) 111 if (walk->type != ex->type) 112 continue; 112 continue; 113 if (walk->major != ex->major) 113 if (walk->major != ex->major) 114 continue; 114 continue; 115 if (walk->minor != ex->minor) 115 if (walk->minor != ex->minor) 116 continue; 116 continue; 117 117 118 walk->access |= ex->access; 118 walk->access |= ex->access; 119 kfree(excopy); 119 kfree(excopy); 120 excopy = NULL; 120 excopy = NULL; 121 } 121 } 122 122 123 if (excopy != NULL) 123 if (excopy != NULL) 124 list_add_tail_rcu(&excopy->lis 124 list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions); 125 return 0; 125 return 0; 126 } 126 } 127 127 128 /* 128 /* 129 * called under devcgroup_mutex 129 * called under devcgroup_mutex 130 */ 130 */ 131 static void dev_exception_rm(struct dev_cgroup 131 static void dev_exception_rm(struct dev_cgroup *dev_cgroup, 132 struct dev_except 132 struct dev_exception_item *ex) 133 { 133 { 134 struct dev_exception_item *walk, *tmp; 134 struct dev_exception_item *walk, *tmp; 135 135 136 lockdep_assert_held(&devcgroup_mutex); 136 lockdep_assert_held(&devcgroup_mutex); 137 137 138 list_for_each_entry_safe(walk, tmp, &d 138 list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) { 139 if (walk->type != ex->type) 139 if (walk->type != ex->type) 140 continue; 140 continue; 141 if (walk->major != ex->major) 141 if (walk->major != ex->major) 142 continue; 142 continue; 143 if (walk->minor != ex->minor) 143 if (walk->minor != ex->minor) 144 continue; 144 continue; 145 145 146 walk->access &= ~ex->access; 146 walk->access &= ~ex->access; 147 if (!walk->access) { 147 if (!walk->access) { 148 list_del_rcu(&walk->li 148 list_del_rcu(&walk->list); 149 kfree_rcu(walk, rcu); 149 kfree_rcu(walk, rcu); 150 } 150 } 151 } 151 } 152 } 152 } 153 153 154 static void __dev_exception_clean(struct dev_c 154 static void __dev_exception_clean(struct dev_cgroup *dev_cgroup) 155 { 155 { 156 struct dev_exception_item *ex, *tmp; 156 struct dev_exception_item *ex, *tmp; 157 157 158 list_for_each_entry_safe(ex, tmp, &dev 158 list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) { 159 list_del_rcu(&ex->list); 159 list_del_rcu(&ex->list); 160 kfree_rcu(ex, rcu); 160 kfree_rcu(ex, rcu); 161 } 161 } 162 } 162 } 163 163 164 /** 164 /** 165 * dev_exception_clean - frees all entries of 165 * dev_exception_clean - frees all entries of the exception list 166 * @dev_cgroup: dev_cgroup with the exception 166 * @dev_cgroup: dev_cgroup with the exception list to be cleaned 167 * 167 * 168 * called under devcgroup_mutex 168 * called under devcgroup_mutex 169 */ 169 */ 170 static void dev_exception_clean(struct dev_cgr 170 static void dev_exception_clean(struct dev_cgroup *dev_cgroup) 171 { 171 { 172 lockdep_assert_held(&devcgroup_mutex); 172 lockdep_assert_held(&devcgroup_mutex); 173 173 174 __dev_exception_clean(dev_cgroup); 174 __dev_exception_clean(dev_cgroup); 175 } 175 } 176 176 177 static inline bool is_devcg_online(const struc 177 static inline bool is_devcg_online(const struct dev_cgroup *devcg) 178 { 178 { 179 return (devcg->behavior != DEVCG_DEFAU 179 return (devcg->behavior != DEVCG_DEFAULT_NONE); 180 } 180 } 181 181 182 /** 182 /** 183 * devcgroup_online - initializes devcgroup's 183 * devcgroup_online - initializes devcgroup's behavior and exceptions based on 184 * parent's 184 * parent's 185 * @css: css getting online 185 * @css: css getting online 186 * returns 0 in case of success, error code ot 186 * returns 0 in case of success, error code otherwise 187 */ 187 */ 188 static int devcgroup_online(struct cgroup_subs 188 static int devcgroup_online(struct cgroup_subsys_state *css) 189 { 189 { 190 struct dev_cgroup *dev_cgroup = css_to 190 struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); 191 struct dev_cgroup *parent_dev_cgroup = 191 struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css->parent); 192 int ret = 0; 192 int ret = 0; 193 193 194 mutex_lock(&devcgroup_mutex); 194 mutex_lock(&devcgroup_mutex); 195 195 196 if (parent_dev_cgroup == NULL) 196 if (parent_dev_cgroup == NULL) 197 dev_cgroup->behavior = DEVCG_D 197 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW; 198 else { 198 else { 199 ret = dev_exceptions_copy(&dev 199 ret = dev_exceptions_copy(&dev_cgroup->exceptions, 200 &par 200 &parent_dev_cgroup->exceptions); 201 if (!ret) 201 if (!ret) 202 dev_cgroup->behavior = 202 dev_cgroup->behavior = parent_dev_cgroup->behavior; 203 } 203 } 204 mutex_unlock(&devcgroup_mutex); 204 mutex_unlock(&devcgroup_mutex); 205 205 206 return ret; 206 return ret; 207 } 207 } 208 208 209 static void devcgroup_offline(struct cgroup_su 209 static void devcgroup_offline(struct cgroup_subsys_state *css) 210 { 210 { 211 struct dev_cgroup *dev_cgroup = css_to 211 struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); 212 212 213 mutex_lock(&devcgroup_mutex); 213 mutex_lock(&devcgroup_mutex); 214 dev_cgroup->behavior = DEVCG_DEFAULT_N 214 dev_cgroup->behavior = DEVCG_DEFAULT_NONE; 215 mutex_unlock(&devcgroup_mutex); 215 mutex_unlock(&devcgroup_mutex); 216 } 216 } 217 217 218 /* 218 /* 219 * called from kernel/cgroup/cgroup.c with cgr 219 * called from kernel/cgroup/cgroup.c with cgroup_lock() held. 220 */ 220 */ 221 static struct cgroup_subsys_state * 221 static struct cgroup_subsys_state * 222 devcgroup_css_alloc(struct cgroup_subsys_state 222 devcgroup_css_alloc(struct cgroup_subsys_state *parent_css) 223 { 223 { 224 struct dev_cgroup *dev_cgroup; 224 struct dev_cgroup *dev_cgroup; 225 225 226 dev_cgroup = kzalloc(sizeof(*dev_cgrou 226 dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL); 227 if (!dev_cgroup) 227 if (!dev_cgroup) 228 return ERR_PTR(-ENOMEM); 228 return ERR_PTR(-ENOMEM); 229 INIT_LIST_HEAD(&dev_cgroup->exceptions 229 INIT_LIST_HEAD(&dev_cgroup->exceptions); 230 dev_cgroup->behavior = DEVCG_DEFAULT_N 230 dev_cgroup->behavior = DEVCG_DEFAULT_NONE; 231 231 232 return &dev_cgroup->css; 232 return &dev_cgroup->css; 233 } 233 } 234 234 235 static void devcgroup_css_free(struct cgroup_s 235 static void devcgroup_css_free(struct cgroup_subsys_state *css) 236 { 236 { 237 struct dev_cgroup *dev_cgroup = css_to 237 struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); 238 238 239 __dev_exception_clean(dev_cgroup); 239 __dev_exception_clean(dev_cgroup); 240 kfree(dev_cgroup); 240 kfree(dev_cgroup); 241 } 241 } 242 242 243 #define DEVCG_ALLOW 1 243 #define DEVCG_ALLOW 1 244 #define DEVCG_DENY 2 244 #define DEVCG_DENY 2 245 #define DEVCG_LIST 3 245 #define DEVCG_LIST 3 246 246 247 #define MAJMINLEN 13 247 #define MAJMINLEN 13 248 #define ACCLEN 4 248 #define ACCLEN 4 249 249 250 static void set_access(char *acc, short access 250 static void set_access(char *acc, short access) 251 { 251 { 252 int idx = 0; 252 int idx = 0; 253 memset(acc, 0, ACCLEN); 253 memset(acc, 0, ACCLEN); 254 if (access & DEVCG_ACC_READ) 254 if (access & DEVCG_ACC_READ) 255 acc[idx++] = 'r'; 255 acc[idx++] = 'r'; 256 if (access & DEVCG_ACC_WRITE) 256 if (access & DEVCG_ACC_WRITE) 257 acc[idx++] = 'w'; 257 acc[idx++] = 'w'; 258 if (access & DEVCG_ACC_MKNOD) 258 if (access & DEVCG_ACC_MKNOD) 259 acc[idx++] = 'm'; 259 acc[idx++] = 'm'; 260 } 260 } 261 261 262 static char type_to_char(short type) 262 static char type_to_char(short type) 263 { 263 { 264 if (type == DEVCG_DEV_ALL) 264 if (type == DEVCG_DEV_ALL) 265 return 'a'; 265 return 'a'; 266 if (type == DEVCG_DEV_CHAR) 266 if (type == DEVCG_DEV_CHAR) 267 return 'c'; 267 return 'c'; 268 if (type == DEVCG_DEV_BLOCK) 268 if (type == DEVCG_DEV_BLOCK) 269 return 'b'; 269 return 'b'; 270 return 'X'; 270 return 'X'; 271 } 271 } 272 272 273 static void set_majmin(char *str, unsigned m) 273 static void set_majmin(char *str, unsigned m) 274 { 274 { 275 if (m == ~0) 275 if (m == ~0) 276 strcpy(str, "*"); 276 strcpy(str, "*"); 277 else 277 else 278 sprintf(str, "%u", m); 278 sprintf(str, "%u", m); 279 } 279 } 280 280 281 static int devcgroup_seq_show(struct seq_file 281 static int devcgroup_seq_show(struct seq_file *m, void *v) 282 { 282 { 283 struct dev_cgroup *devcgroup = css_to_ 283 struct dev_cgroup *devcgroup = css_to_devcgroup(seq_css(m)); 284 struct dev_exception_item *ex; 284 struct dev_exception_item *ex; 285 char maj[MAJMINLEN], min[MAJMINLEN], a 285 char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; 286 286 287 rcu_read_lock(); 287 rcu_read_lock(); 288 /* 288 /* 289 * To preserve the compatibility: 289 * To preserve the compatibility: 290 * - Only show the "all devices" when 290 * - Only show the "all devices" when the default policy is to allow 291 * - List the exceptions in case the d 291 * - List the exceptions in case the default policy is to deny 292 * This way, the file remains as a "wh 292 * This way, the file remains as a "whitelist of devices" 293 */ 293 */ 294 if (devcgroup->behavior == DEVCG_DEFAU 294 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { 295 set_access(acc, DEVCG_ACC_MASK 295 set_access(acc, DEVCG_ACC_MASK); 296 set_majmin(maj, ~0); 296 set_majmin(maj, ~0); 297 set_majmin(min, ~0); 297 set_majmin(min, ~0); 298 seq_printf(m, "%c %s:%s %s\n", 298 seq_printf(m, "%c %s:%s %s\n", type_to_char(DEVCG_DEV_ALL), 299 maj, min, acc); 299 maj, min, acc); 300 } else { 300 } else { 301 list_for_each_entry_rcu(ex, &d 301 list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) { 302 set_access(acc, ex->ac 302 set_access(acc, ex->access); 303 set_majmin(maj, ex->ma 303 set_majmin(maj, ex->major); 304 set_majmin(min, ex->mi 304 set_majmin(min, ex->minor); 305 seq_printf(m, "%c %s:% 305 seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type), 306 maj, min, a 306 maj, min, acc); 307 } 307 } 308 } 308 } 309 rcu_read_unlock(); 309 rcu_read_unlock(); 310 310 311 return 0; 311 return 0; 312 } 312 } 313 313 314 /** 314 /** 315 * match_exception - iterates the excepti 315 * match_exception - iterates the exception list trying to find a complete match 316 * @exceptions: list of exceptions 316 * @exceptions: list of exceptions 317 * @type: device type (DEVCG_DEV_BLOCK or DEVC 317 * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR) 318 * @major: device file major number, ~0 to mat 318 * @major: device file major number, ~0 to match all 319 * @minor: device file minor number, ~0 to mat 319 * @minor: device file minor number, ~0 to match all 320 * @access: permission mask (DEVCG_ACC_READ, D 320 * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD) 321 * 321 * 322 * It is considered a complete match if an exc 322 * It is considered a complete match if an exception is found that will 323 * contain the entire range of provided parame 323 * contain the entire range of provided parameters. 324 * 324 * 325 * Return: true in case it matches an exceptio 325 * Return: true in case it matches an exception completely 326 */ 326 */ 327 static bool match_exception(struct list_head * 327 static bool match_exception(struct list_head *exceptions, short type, 328 u32 major, u32 min 328 u32 major, u32 minor, short access) 329 { 329 { 330 struct dev_exception_item *ex; 330 struct dev_exception_item *ex; 331 331 332 list_for_each_entry_rcu(ex, exceptions 332 list_for_each_entry_rcu(ex, exceptions, list) { 333 if ((type & DEVCG_DEV_BLOCK) & 333 if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK)) 334 continue; 334 continue; 335 if ((type & DEVCG_DEV_CHAR) && 335 if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR)) 336 continue; 336 continue; 337 if (ex->major != ~0 && ex->maj 337 if (ex->major != ~0 && ex->major != major) 338 continue; 338 continue; 339 if (ex->minor != ~0 && ex->min 339 if (ex->minor != ~0 && ex->minor != minor) 340 continue; 340 continue; 341 /* provided access cannot have 341 /* provided access cannot have more than the exception rule */ 342 if (access & (~ex->access)) 342 if (access & (~ex->access)) 343 continue; 343 continue; 344 return true; 344 return true; 345 } 345 } 346 return false; 346 return false; 347 } 347 } 348 348 349 /** 349 /** 350 * match_exception_partial - iterates the exce 350 * match_exception_partial - iterates the exception list trying to find a partial match 351 * @exceptions: list of exceptions 351 * @exceptions: list of exceptions 352 * @type: device type (DEVCG_DEV_BLOCK or DEVC 352 * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR) 353 * @major: device file major number, ~0 to mat 353 * @major: device file major number, ~0 to match all 354 * @minor: device file minor number, ~0 to mat 354 * @minor: device file minor number, ~0 to match all 355 * @access: permission mask (DEVCG_ACC_READ, D 355 * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD) 356 * 356 * 357 * It is considered a partial match if an exce 357 * It is considered a partial match if an exception's range is found to 358 * contain *any* of the devices specified by p 358 * contain *any* of the devices specified by provided parameters. This is 359 * used to make sure no extra access is being 359 * used to make sure no extra access is being granted that is forbidden by 360 * any of the exception list. 360 * any of the exception list. 361 * 361 * 362 * Return: true in case the provided range mat 362 * Return: true in case the provided range mat matches an exception completely 363 */ 363 */ 364 static bool match_exception_partial(struct lis 364 static bool match_exception_partial(struct list_head *exceptions, short type, 365 u32 major, 365 u32 major, u32 minor, short access) 366 { 366 { 367 struct dev_exception_item *ex; 367 struct dev_exception_item *ex; 368 368 369 list_for_each_entry_rcu(ex, exceptions 369 list_for_each_entry_rcu(ex, exceptions, list, 370 lockdep_is_hel 370 lockdep_is_held(&devcgroup_mutex)) { 371 if ((type & DEVCG_DEV_BLOCK) & 371 if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK)) 372 continue; 372 continue; 373 if ((type & DEVCG_DEV_CHAR) && 373 if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR)) 374 continue; 374 continue; 375 /* 375 /* 376 * We must be sure that both t 376 * We must be sure that both the exception and the provided 377 * range aren't masking all de 377 * range aren't masking all devices 378 */ 378 */ 379 if (ex->major != ~0 && major ! 379 if (ex->major != ~0 && major != ~0 && ex->major != major) 380 continue; 380 continue; 381 if (ex->minor != ~0 && minor ! 381 if (ex->minor != ~0 && minor != ~0 && ex->minor != minor) 382 continue; 382 continue; 383 /* 383 /* 384 * In order to make sure the p 384 * In order to make sure the provided range isn't matching 385 * an exception, all its acces 385 * an exception, all its access bits shouldn't match the 386 * exception's access bits 386 * exception's access bits 387 */ 387 */ 388 if (!(access & ex->access)) 388 if (!(access & ex->access)) 389 continue; 389 continue; 390 return true; 390 return true; 391 } 391 } 392 return false; 392 return false; 393 } 393 } 394 394 395 /** 395 /** 396 * verify_new_ex - verifies if a new exception 396 * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions 397 * @dev_cgroup: dev cgroup to be tested agains 397 * @dev_cgroup: dev cgroup to be tested against 398 * @refex: new exception 398 * @refex: new exception 399 * @behavior: behavior of the exception's dev_ 399 * @behavior: behavior of the exception's dev_cgroup 400 * 400 * 401 * This is used to make sure a child cgroup wo 401 * This is used to make sure a child cgroup won't have more privileges 402 * than its parent 402 * than its parent 403 */ 403 */ 404 static bool verify_new_ex(struct dev_cgroup *d 404 static bool verify_new_ex(struct dev_cgroup *dev_cgroup, 405 struct dev_exception 405 struct dev_exception_item *refex, 406 enum devcg_behavior 406 enum devcg_behavior behavior) 407 { 407 { 408 bool match = false; 408 bool match = false; 409 409 410 RCU_LOCKDEP_WARN(!rcu_read_lock_held() 410 RCU_LOCKDEP_WARN(!rcu_read_lock_held() && 411 !lockdep_is_held(&dev 411 !lockdep_is_held(&devcgroup_mutex), 412 "device_cgroup:verify 412 "device_cgroup:verify_new_ex called without proper synchronization"); 413 413 414 if (dev_cgroup->behavior == DEVCG_DEFA 414 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) { 415 if (behavior == DEVCG_DEFAULT_ 415 if (behavior == DEVCG_DEFAULT_ALLOW) { 416 /* 416 /* 417 * new exception in th 417 * new exception in the child doesn't matter, only 418 * adding extra restri 418 * adding extra restrictions 419 */ 419 */ 420 return true; 420 return true; 421 } else { 421 } else { 422 /* 422 /* 423 * new exception in th 423 * new exception in the child will add more devices 424 * that can be accesse 424 * that can be accessed, so it can't match any of 425 * parent's exceptions 425 * parent's exceptions, even slightly 426 */ 426 */ 427 match = match_exceptio 427 match = match_exception_partial(&dev_cgroup->exceptions, 428 428 refex->type, 429 429 refex->major, 430 430 refex->minor, 431 431 refex->access); 432 432 433 if (match) 433 if (match) 434 return false; 434 return false; 435 return true; 435 return true; 436 } 436 } 437 } else { 437 } else { 438 /* 438 /* 439 * Only behavior == DEVCG_DEFA 439 * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore 440 * the new exception will add 440 * the new exception will add access to more devices and must 441 * be contained completely in 441 * be contained completely in an parent's exception to be 442 * allowed 442 * allowed 443 */ 443 */ 444 match = match_exception(&dev_c 444 match = match_exception(&dev_cgroup->exceptions, refex->type, 445 refex- 445 refex->major, refex->minor, 446 refex- 446 refex->access); 447 447 448 if (match) 448 if (match) 449 /* parent has an excep 449 /* parent has an exception that matches the proposed */ 450 return true; 450 return true; 451 else 451 else 452 return false; 452 return false; 453 } 453 } 454 return false; 454 return false; 455 } 455 } 456 456 457 /* 457 /* 458 * parent_has_perm: 458 * parent_has_perm: 459 * when adding a new allow rule to a device ex 459 * when adding a new allow rule to a device exception list, the rule 460 * must be allowed in the parent device 460 * must be allowed in the parent device 461 */ 461 */ 462 static int parent_has_perm(struct dev_cgroup * 462 static int parent_has_perm(struct dev_cgroup *childcg, 463 struct dev_e 463 struct dev_exception_item *ex) 464 { 464 { 465 struct dev_cgroup *parent = css_to_dev 465 struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent); 466 466 467 if (!parent) 467 if (!parent) 468 return 1; 468 return 1; 469 return verify_new_ex(parent, ex, child 469 return verify_new_ex(parent, ex, childcg->behavior); 470 } 470 } 471 471 472 /** 472 /** 473 * parent_allows_removal - verify if it's ok t 473 * parent_allows_removal - verify if it's ok to remove an exception 474 * @childcg: child cgroup from where the excep 474 * @childcg: child cgroup from where the exception will be removed 475 * @ex: exception being removed 475 * @ex: exception being removed 476 * 476 * 477 * When removing an exception in cgroups with 477 * When removing an exception in cgroups with default ALLOW policy, it must 478 * be checked if removing it will give the chi 478 * be checked if removing it will give the child cgroup more access than the 479 * parent. 479 * parent. 480 * 480 * 481 * Return: true if it's ok to remove exception 481 * Return: true if it's ok to remove exception, false otherwise 482 */ 482 */ 483 static bool parent_allows_removal(struct dev_c 483 static bool parent_allows_removal(struct dev_cgroup *childcg, 484 struct dev_e 484 struct dev_exception_item *ex) 485 { 485 { 486 struct dev_cgroup *parent = css_to_dev 486 struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent); 487 487 488 if (!parent) 488 if (!parent) 489 return true; 489 return true; 490 490 491 /* It's always allowed to remove acces 491 /* It's always allowed to remove access to devices */ 492 if (childcg->behavior == DEVCG_DEFAULT 492 if (childcg->behavior == DEVCG_DEFAULT_DENY) 493 return true; 493 return true; 494 494 495 /* 495 /* 496 * Make sure you're not removing part 496 * Make sure you're not removing part or a whole exception existing in 497 * the parent cgroup 497 * the parent cgroup 498 */ 498 */ 499 return !match_exception_partial(&paren 499 return !match_exception_partial(&parent->exceptions, ex->type, 500 ex->ma 500 ex->major, ex->minor, ex->access); 501 } 501 } 502 502 503 /** 503 /** 504 * may_allow_all - checks if it's possible to 504 * may_allow_all - checks if it's possible to change the behavior to 505 * allow based on parent's rul 505 * allow based on parent's rules. 506 * @parent: device cgroup's parent 506 * @parent: device cgroup's parent 507 * returns: != 0 in case it's allowed, 0 other 507 * returns: != 0 in case it's allowed, 0 otherwise 508 */ 508 */ 509 static inline int may_allow_all(struct dev_cgr 509 static inline int may_allow_all(struct dev_cgroup *parent) 510 { 510 { 511 if (!parent) 511 if (!parent) 512 return 1; 512 return 1; 513 return parent->behavior == DEVCG_DEFAU 513 return parent->behavior == DEVCG_DEFAULT_ALLOW; 514 } 514 } 515 515 516 /** 516 /** 517 * revalidate_active_exceptions - walks throug 517 * revalidate_active_exceptions - walks through the active exception list and 518 * revalidates 518 * revalidates the exceptions based on parent's 519 * behavior and 519 * behavior and exceptions. The exceptions that 520 * are no longe 520 * are no longer valid will be removed. 521 * Called with 521 * Called with devcgroup_mutex held. 522 * @devcg: cgroup which exceptions will be che 522 * @devcg: cgroup which exceptions will be checked 523 * 523 * 524 * This is one of the three key functions for 524 * This is one of the three key functions for hierarchy implementation. 525 * This function is responsible for re-evaluat 525 * This function is responsible for re-evaluating all the cgroup's active 526 * exceptions due to a parent's exception chan 526 * exceptions due to a parent's exception change. 527 * Refer to Documentation/admin-guide/cgroup-v 527 * Refer to Documentation/admin-guide/cgroup-v1/devices.rst for more details. 528 */ 528 */ 529 static void revalidate_active_exceptions(struc 529 static void revalidate_active_exceptions(struct dev_cgroup *devcg) 530 { 530 { 531 struct dev_exception_item *ex; 531 struct dev_exception_item *ex; 532 struct list_head *this, *tmp; 532 struct list_head *this, *tmp; 533 533 534 list_for_each_safe(this, tmp, &devcg-> 534 list_for_each_safe(this, tmp, &devcg->exceptions) { 535 ex = container_of(this, struct 535 ex = container_of(this, struct dev_exception_item, list); 536 if (!parent_has_perm(devcg, ex 536 if (!parent_has_perm(devcg, ex)) 537 dev_exception_rm(devcg 537 dev_exception_rm(devcg, ex); 538 } 538 } 539 } 539 } 540 540 541 /** 541 /** 542 * propagate_exception - propagates a new exce 542 * propagate_exception - propagates a new exception to the children 543 * @devcg_root: device cgroup that added a new 543 * @devcg_root: device cgroup that added a new exception 544 * @ex: new exception to be propagated 544 * @ex: new exception to be propagated 545 * 545 * 546 * returns: 0 in case of success, != 0 in case 546 * returns: 0 in case of success, != 0 in case of error 547 */ 547 */ 548 static int propagate_exception(struct dev_cgro 548 static int propagate_exception(struct dev_cgroup *devcg_root, 549 struct dev_exce 549 struct dev_exception_item *ex) 550 { 550 { 551 struct cgroup_subsys_state *pos; 551 struct cgroup_subsys_state *pos; 552 int rc = 0; 552 int rc = 0; 553 553 554 rcu_read_lock(); 554 rcu_read_lock(); 555 555 556 css_for_each_descendant_pre(pos, &devc 556 css_for_each_descendant_pre(pos, &devcg_root->css) { 557 struct dev_cgroup *devcg = css 557 struct dev_cgroup *devcg = css_to_devcgroup(pos); 558 558 559 /* 559 /* 560 * Because devcgroup_mutex is 560 * Because devcgroup_mutex is held, no devcg will become 561 * online or offline during th 561 * online or offline during the tree walk (see on/offline 562 * methods), and online ones a 562 * methods), and online ones are safe to access outside RCU 563 * read lock without bumping r 563 * read lock without bumping refcnt. 564 */ 564 */ 565 if (pos == &devcg_root->css || 565 if (pos == &devcg_root->css || !is_devcg_online(devcg)) 566 continue; 566 continue; 567 567 568 rcu_read_unlock(); 568 rcu_read_unlock(); 569 569 570 /* 570 /* 571 * in case both root's behavio 571 * in case both root's behavior and devcg is allow, a new 572 * restriction means adding to 572 * restriction means adding to the exception list 573 */ 573 */ 574 if (devcg_root->behavior == DE 574 if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW && 575 devcg->behavior == DEVCG_D 575 devcg->behavior == DEVCG_DEFAULT_ALLOW) { 576 rc = dev_exception_add 576 rc = dev_exception_add(devcg, ex); 577 if (rc) 577 if (rc) 578 return rc; 578 return rc; 579 } else { 579 } else { 580 /* 580 /* 581 * in the other possib 581 * in the other possible cases: 582 * root's behavior: al 582 * root's behavior: allow, devcg's: deny 583 * root's behavior: de 583 * root's behavior: deny, devcg's: deny 584 * the exception will 584 * the exception will be removed 585 */ 585 */ 586 dev_exception_rm(devcg 586 dev_exception_rm(devcg, ex); 587 } 587 } 588 revalidate_active_exceptions(d 588 revalidate_active_exceptions(devcg); 589 589 590 rcu_read_lock(); 590 rcu_read_lock(); 591 } 591 } 592 592 593 rcu_read_unlock(); 593 rcu_read_unlock(); 594 return rc; 594 return rc; 595 } 595 } 596 596 597 /* 597 /* 598 * Modify the exception list using allow/deny 598 * Modify the exception list using allow/deny rules. 599 * CAP_SYS_ADMIN is needed for this. It's at 599 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD 600 * so we can give a container CAP_MKNOD to let 600 * so we can give a container CAP_MKNOD to let it create devices but not 601 * modify the exception list. 601 * modify the exception list. 602 * It seems likely we'll want to add a CAP_CON 602 * It seems likely we'll want to add a CAP_CONTAINER capability to allow 603 * us to also grant CAP_SYS_ADMIN to container 603 * us to also grant CAP_SYS_ADMIN to containers without giving away the 604 * device exception list controls, but for now 604 * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN 605 * 605 * 606 * Taking rules away is always allowed (given 606 * Taking rules away is always allowed (given CAP_SYS_ADMIN). Granting 607 * new access is only allowed if you're in the 607 * new access is only allowed if you're in the top-level cgroup, or your 608 * parent cgroup has the access you're asking 608 * parent cgroup has the access you're asking for. 609 */ 609 */ 610 static int devcgroup_update_access(struct dev_ 610 static int devcgroup_update_access(struct dev_cgroup *devcgroup, 611 int filetyp 611 int filetype, char *buffer) 612 { 612 { 613 const char *b; 613 const char *b; 614 char temp[12]; /* 11 + 1 char 614 char temp[12]; /* 11 + 1 characters needed for a u32 */ 615 int count, rc = 0; 615 int count, rc = 0; 616 struct dev_exception_item ex; 616 struct dev_exception_item ex; 617 struct dev_cgroup *parent = css_to_dev 617 struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent); 618 struct dev_cgroup tmp_devcgrp; 618 struct dev_cgroup tmp_devcgrp; 619 619 620 if (!capable(CAP_SYS_ADMIN)) 620 if (!capable(CAP_SYS_ADMIN)) 621 return -EPERM; 621 return -EPERM; 622 622 623 memset(&ex, 0, sizeof(ex)); 623 memset(&ex, 0, sizeof(ex)); 624 memset(&tmp_devcgrp, 0, sizeof(tmp_dev 624 memset(&tmp_devcgrp, 0, sizeof(tmp_devcgrp)); 625 b = buffer; 625 b = buffer; 626 626 627 switch (*b) { 627 switch (*b) { 628 case 'a': 628 case 'a': 629 switch (filetype) { 629 switch (filetype) { 630 case DEVCG_ALLOW: 630 case DEVCG_ALLOW: 631 if (css_has_online_chi 631 if (css_has_online_children(&devcgroup->css)) 632 return -EINVAL 632 return -EINVAL; 633 633 634 if (!may_allow_all(par 634 if (!may_allow_all(parent)) 635 return -EPERM; 635 return -EPERM; 636 if (!parent) { 636 if (!parent) { 637 devcgroup->beh 637 devcgroup->behavior = DEVCG_DEFAULT_ALLOW; 638 dev_exception_ 638 dev_exception_clean(devcgroup); 639 break; 639 break; 640 } 640 } 641 641 642 INIT_LIST_HEAD(&tmp_de 642 INIT_LIST_HEAD(&tmp_devcgrp.exceptions); 643 rc = dev_exceptions_co 643 rc = dev_exceptions_copy(&tmp_devcgrp.exceptions, 644 644 &devcgroup->exceptions); 645 if (rc) 645 if (rc) 646 return rc; 646 return rc; 647 dev_exception_clean(de 647 dev_exception_clean(devcgroup); 648 rc = dev_exceptions_co 648 rc = dev_exceptions_copy(&devcgroup->exceptions, 649 649 &parent->exceptions); 650 if (rc) { 650 if (rc) { 651 dev_exceptions 651 dev_exceptions_move(&devcgroup->exceptions, 652 652 &tmp_devcgrp.exceptions); 653 return rc; 653 return rc; 654 } 654 } 655 devcgroup->behavior = 655 devcgroup->behavior = DEVCG_DEFAULT_ALLOW; 656 dev_exception_clean(&t 656 dev_exception_clean(&tmp_devcgrp); 657 break; 657 break; 658 case DEVCG_DENY: 658 case DEVCG_DENY: 659 if (css_has_online_chi 659 if (css_has_online_children(&devcgroup->css)) 660 return -EINVAL 660 return -EINVAL; 661 661 662 dev_exception_clean(de 662 dev_exception_clean(devcgroup); 663 devcgroup->behavior = 663 devcgroup->behavior = DEVCG_DEFAULT_DENY; 664 break; 664 break; 665 default: 665 default: 666 return -EINVAL; 666 return -EINVAL; 667 } 667 } 668 return 0; 668 return 0; 669 case 'b': 669 case 'b': 670 ex.type = DEVCG_DEV_BLOCK; 670 ex.type = DEVCG_DEV_BLOCK; 671 break; 671 break; 672 case 'c': 672 case 'c': 673 ex.type = DEVCG_DEV_CHAR; 673 ex.type = DEVCG_DEV_CHAR; 674 break; 674 break; 675 default: 675 default: 676 return -EINVAL; 676 return -EINVAL; 677 } 677 } 678 b++; 678 b++; 679 if (!isspace(*b)) 679 if (!isspace(*b)) 680 return -EINVAL; 680 return -EINVAL; 681 b++; 681 b++; 682 if (*b == '*') { 682 if (*b == '*') { 683 ex.major = ~0; 683 ex.major = ~0; 684 b++; 684 b++; 685 } else if (isdigit(*b)) { 685 } else if (isdigit(*b)) { 686 memset(temp, 0, sizeof(temp)); 686 memset(temp, 0, sizeof(temp)); 687 for (count = 0; count < sizeof 687 for (count = 0; count < sizeof(temp) - 1; count++) { 688 temp[count] = *b; 688 temp[count] = *b; 689 b++; 689 b++; 690 if (!isdigit(*b)) 690 if (!isdigit(*b)) 691 break; 691 break; 692 } 692 } 693 rc = kstrtou32(temp, 10, &ex.m 693 rc = kstrtou32(temp, 10, &ex.major); 694 if (rc) 694 if (rc) 695 return -EINVAL; 695 return -EINVAL; 696 } else { 696 } else { 697 return -EINVAL; 697 return -EINVAL; 698 } 698 } 699 if (*b != ':') 699 if (*b != ':') 700 return -EINVAL; 700 return -EINVAL; 701 b++; 701 b++; 702 702 703 /* read minor */ 703 /* read minor */ 704 if (*b == '*') { 704 if (*b == '*') { 705 ex.minor = ~0; 705 ex.minor = ~0; 706 b++; 706 b++; 707 } else if (isdigit(*b)) { 707 } else if (isdigit(*b)) { 708 memset(temp, 0, sizeof(temp)); 708 memset(temp, 0, sizeof(temp)); 709 for (count = 0; count < sizeof 709 for (count = 0; count < sizeof(temp) - 1; count++) { 710 temp[count] = *b; 710 temp[count] = *b; 711 b++; 711 b++; 712 if (!isdigit(*b)) 712 if (!isdigit(*b)) 713 break; 713 break; 714 } 714 } 715 rc = kstrtou32(temp, 10, &ex.m 715 rc = kstrtou32(temp, 10, &ex.minor); 716 if (rc) 716 if (rc) 717 return -EINVAL; 717 return -EINVAL; 718 } else { 718 } else { 719 return -EINVAL; 719 return -EINVAL; 720 } 720 } 721 if (!isspace(*b)) 721 if (!isspace(*b)) 722 return -EINVAL; 722 return -EINVAL; 723 for (b++, count = 0; count < 3; count+ 723 for (b++, count = 0; count < 3; count++, b++) { 724 switch (*b) { 724 switch (*b) { 725 case 'r': 725 case 'r': 726 ex.access |= DEVCG_ACC 726 ex.access |= DEVCG_ACC_READ; 727 break; 727 break; 728 case 'w': 728 case 'w': 729 ex.access |= DEVCG_ACC 729 ex.access |= DEVCG_ACC_WRITE; 730 break; 730 break; 731 case 'm': 731 case 'm': 732 ex.access |= DEVCG_ACC 732 ex.access |= DEVCG_ACC_MKNOD; 733 break; 733 break; 734 case '\n': 734 case '\n': 735 case '\0': 735 case '\0': 736 count = 3; 736 count = 3; 737 break; 737 break; 738 default: 738 default: 739 return -EINVAL; 739 return -EINVAL; 740 } 740 } 741 } 741 } 742 742 743 switch (filetype) { 743 switch (filetype) { 744 case DEVCG_ALLOW: 744 case DEVCG_ALLOW: 745 /* 745 /* 746 * If the default policy is to 746 * If the default policy is to allow by default, try to remove 747 * an matching exception inste 747 * an matching exception instead. And be silent about it: we 748 * don't want to break compati 748 * don't want to break compatibility 749 */ 749 */ 750 if (devcgroup->behavior == DEV 750 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { 751 /* Check if the parent 751 /* Check if the parent allows removing it first */ 752 if (!parent_allows_rem 752 if (!parent_allows_removal(devcgroup, &ex)) 753 return -EPERM; 753 return -EPERM; 754 dev_exception_rm(devcg 754 dev_exception_rm(devcgroup, &ex); 755 break; 755 break; 756 } 756 } 757 757 758 if (!parent_has_perm(devcgroup 758 if (!parent_has_perm(devcgroup, &ex)) 759 return -EPERM; 759 return -EPERM; 760 rc = dev_exception_add(devcgro 760 rc = dev_exception_add(devcgroup, &ex); 761 break; 761 break; 762 case DEVCG_DENY: 762 case DEVCG_DENY: 763 /* 763 /* 764 * If the default policy is to 764 * If the default policy is to deny by default, try to remove 765 * an matching exception inste 765 * an matching exception instead. And be silent about it: we 766 * don't want to break compati 766 * don't want to break compatibility 767 */ 767 */ 768 if (devcgroup->behavior == DEV 768 if (devcgroup->behavior == DEVCG_DEFAULT_DENY) 769 dev_exception_rm(devcg 769 dev_exception_rm(devcgroup, &ex); 770 else 770 else 771 rc = dev_exception_add 771 rc = dev_exception_add(devcgroup, &ex); 772 772 773 if (rc) 773 if (rc) 774 break; 774 break; 775 /* we only propagate new restr 775 /* we only propagate new restrictions */ 776 rc = propagate_exception(devcg 776 rc = propagate_exception(devcgroup, &ex); 777 break; 777 break; 778 default: 778 default: 779 rc = -EINVAL; 779 rc = -EINVAL; 780 } 780 } 781 return rc; 781 return rc; 782 } 782 } 783 783 784 static ssize_t devcgroup_access_write(struct k 784 static ssize_t devcgroup_access_write(struct kernfs_open_file *of, 785 char *bu 785 char *buf, size_t nbytes, loff_t off) 786 { 786 { 787 int retval; 787 int retval; 788 788 789 mutex_lock(&devcgroup_mutex); 789 mutex_lock(&devcgroup_mutex); 790 retval = devcgroup_update_access(css_t 790 retval = devcgroup_update_access(css_to_devcgroup(of_css(of)), 791 of_cf 791 of_cft(of)->private, strstrip(buf)); 792 mutex_unlock(&devcgroup_mutex); 792 mutex_unlock(&devcgroup_mutex); 793 return retval ?: nbytes; 793 return retval ?: nbytes; 794 } 794 } 795 795 796 static struct cftype dev_cgroup_files[] = { 796 static struct cftype dev_cgroup_files[] = { 797 { 797 { 798 .name = "allow", 798 .name = "allow", 799 .write = devcgroup_access_writ 799 .write = devcgroup_access_write, 800 .private = DEVCG_ALLOW, 800 .private = DEVCG_ALLOW, 801 }, 801 }, 802 { 802 { 803 .name = "deny", 803 .name = "deny", 804 .write = devcgroup_access_writ 804 .write = devcgroup_access_write, 805 .private = DEVCG_DENY, 805 .private = DEVCG_DENY, 806 }, 806 }, 807 { 807 { 808 .name = "list", 808 .name = "list", 809 .seq_show = devcgroup_seq_show 809 .seq_show = devcgroup_seq_show, 810 .private = DEVCG_LIST, 810 .private = DEVCG_LIST, 811 }, 811 }, 812 { } /* terminate */ 812 { } /* terminate */ 813 }; 813 }; 814 814 815 struct cgroup_subsys devices_cgrp_subsys = { 815 struct cgroup_subsys devices_cgrp_subsys = { 816 .css_alloc = devcgroup_css_alloc, 816 .css_alloc = devcgroup_css_alloc, 817 .css_free = devcgroup_css_free, 817 .css_free = devcgroup_css_free, 818 .css_online = devcgroup_online, 818 .css_online = devcgroup_online, 819 .css_offline = devcgroup_offline, 819 .css_offline = devcgroup_offline, 820 .legacy_cftypes = dev_cgroup_files, 820 .legacy_cftypes = dev_cgroup_files, 821 }; 821 }; 822 822 823 /** 823 /** 824 * devcgroup_legacy_check_permission - checks 824 * devcgroup_legacy_check_permission - checks if an inode operation is permitted 825 * @type: device type 825 * @type: device type 826 * @major: device major number 826 * @major: device major number 827 * @minor: device minor number 827 * @minor: device minor number 828 * @access: combination of DEVCG_ACC_WRITE, DE 828 * @access: combination of DEVCG_ACC_WRITE, DEVCG_ACC_READ and DEVCG_ACC_MKNOD 829 * 829 * 830 * returns 0 on success, -EPERM case the opera 830 * returns 0 on success, -EPERM case the operation is not permitted 831 */ 831 */ 832 static int devcgroup_legacy_check_permission(s 832 static int devcgroup_legacy_check_permission(short type, u32 major, u32 minor, 833 short 833 short access) 834 { 834 { 835 struct dev_cgroup *dev_cgroup; 835 struct dev_cgroup *dev_cgroup; 836 bool rc; 836 bool rc; 837 837 838 rcu_read_lock(); 838 rcu_read_lock(); 839 dev_cgroup = task_devcgroup(current); 839 dev_cgroup = task_devcgroup(current); 840 if (dev_cgroup->behavior == DEVCG_DEFA 840 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) 841 /* Can't match any of the exce 841 /* Can't match any of the exceptions, even partially */ 842 rc = !match_exception_partial( 842 rc = !match_exception_partial(&dev_cgroup->exceptions, 843 843 type, major, minor, access); 844 else 844 else 845 /* Need to match completely on 845 /* Need to match completely one exception to be allowed */ 846 rc = match_exception(&dev_cgro 846 rc = match_exception(&dev_cgroup->exceptions, type, major, 847 minor, ac 847 minor, access); 848 rcu_read_unlock(); 848 rcu_read_unlock(); 849 849 850 if (!rc) 850 if (!rc) 851 return -EPERM; 851 return -EPERM; 852 852 853 return 0; 853 return 0; 854 } 854 } 855 855 856 #endif /* CONFIG_CGROUP_DEVICE */ 856 #endif /* CONFIG_CGROUP_DEVICE */ 857 857 858 #if defined(CONFIG_CGROUP_DEVICE) || defined(C 858 #if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) 859 859 860 int devcgroup_check_permission(short type, u32 860 int devcgroup_check_permission(short type, u32 major, u32 minor, short access) 861 { 861 { 862 int rc = BPF_CGROUP_RUN_PROG_DEVICE_CG 862 int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access); 863 863 864 if (rc) 864 if (rc) 865 return rc; 865 return rc; 866 866 867 #ifdef CONFIG_CGROUP_DEVICE 867 #ifdef CONFIG_CGROUP_DEVICE 868 return devcgroup_legacy_check_permissi 868 return devcgroup_legacy_check_permission(type, major, minor, access); 869 869 870 #else /* CONFIG_CGROUP_DEVICE */ 870 #else /* CONFIG_CGROUP_DEVICE */ 871 return 0; 871 return 0; 872 872 873 #endif /* CONFIG_CGROUP_DEVICE */ 873 #endif /* CONFIG_CGROUP_DEVICE */ 874 } 874 } 875 EXPORT_SYMBOL(devcgroup_check_permission); 875 EXPORT_SYMBOL(devcgroup_check_permission); 876 #endif /* defined(CONFIG_CGROUP_DEVICE) || def 876 #endif /* defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) */ 877 877
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.