1 /* Reduced and adapted from Linux: fs/proc/inode.c: proc_reg_open
2 (GPL v2.0). */
3
4 /* Types. */
5
6 typedef unsigned char u8;
7 typedef _Bool bool;
8 typedef unsigned int gfp_t;
9
10 struct file;
11 struct kmem_cache;
12 struct proc_dir_entry;
13
14 struct inode { /* [...snip...] */ };
15
16 enum {
17 PROC_ENTRY_PERMANENT = 1U << 0,
18 };
19
20 struct proc_ops {
21 /* [...snip...] */
22 int (*proc_open)(struct inode *, struct file *);
23 /* [...snip...] */
24 int (*proc_release)(struct inode *, struct file *);
25 /* [...snip...] */
26 };
27
28 struct proc_dir_entry {
29 /* [...snip...] */
30 struct completion *pde_unload_completion;
31 /* [...snip...] */
32 union {
33 const struct proc_ops *proc_ops;
34 const struct file_operations *proc_dir_ops;
35 };
36 /* [...snip...] */
37 u8 flags;
38 /* [...snip...] */
39 };
40
41 struct pde_opener {
42 /* [...snip...] */
43 struct file *file;
44 /* [...snip...] */
45 };
46
47 struct proc_inode {
48 /* [...snip...] */
49 struct proc_dir_entry *pde;
50 /* [...snip...] */
51 struct inode vfs_inode;
52 };
53
54 /* Data. */
55
56 static struct kmem_cache *pde_opener_cache __attribute__((__section__(".data..ro_after_init")));
57
58 /* Functions. */
59
60 void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags) __attribute__((__malloc__));
61 void kmem_cache_free(struct kmem_cache *, void *);
62
pde_is_permanent(const struct proc_dir_entry * pde)63 static inline bool pde_is_permanent(const struct proc_dir_entry *pde)
64 {
65 return pde->flags & PROC_ENTRY_PERMANENT;
66 }
67
PROC_I(const struct inode * inode)68 static inline struct proc_inode *PROC_I(const struct inode *inode)
69 {
70 void *__mptr = (void *)(inode);
71 return ((struct proc_inode *)(__mptr - __builtin_offsetof(struct proc_inode, vfs_inode)));
72 }
73
PDE(const struct inode * inode)74 static inline struct proc_dir_entry *PDE(const struct inode *inode)
75 {
76 return PROC_I(inode)->pde;
77 }
78
79 /* We don't want to emit bogus use of uninitialized value 'pdeo'
80 warnings from -Wanalyzer-use-of-uninitialized-value in this function;
81 these would require following infeasible paths in which "release" is
82 first NULL (to avoid the initialization of "pdeo") and then is non-NULL
83 (to access "pdeo").
84
85 "release" is sufficiently complicated in this function to hit the
86 complexity limit for symbolic values during enode exploration. */
87
proc_reg_open(struct inode * inode,struct file * file)88 static int proc_reg_open(struct inode *inode, struct file *file)
89 {
90 struct proc_dir_entry *pde = PDE(inode);
91 int rv = 0;
92 typeof(((struct proc_ops*)0)->proc_open) open;
93 typeof(((struct proc_ops*)0)->proc_release) release;
94 struct pde_opener *pdeo;
95
96 if (pde_is_permanent(pde)) {
97 open = pde->proc_ops->proc_open;
98 if (open)
99 rv = open(inode, file);
100 return rv;
101 }
102
103 /* [...snip...] */
104
105 release = pde->proc_ops->proc_release;
106 if (release) {
107 pdeo = kmem_cache_alloc(pde_opener_cache,
108 ((( gfp_t)(0x400u|0x800u))
109 | (( gfp_t)0x40u)
110 | (( gfp_t)0x80u)));
111 if (!pdeo) {
112 rv = -12;
113 goto out_unuse;
114 }
115 }
116
117 open = pde->proc_ops->proc_open;
118 if (open)
119 rv = open(inode, file);
120
121 if (release) {
122 if (rv == 0) {
123
124 pdeo->file = file; /* { dg-bogus "uninit" } */
125 /* [...snip...] */
126 } else
127 kmem_cache_free(pde_opener_cache, pdeo); /* { dg-bogus "uninit" } */
128 }
129
130 out_unuse:
131 /* [...snip...] */
132 return rv;
133 }
134