| /* Reduced and adapted from Linux: fs/proc/inode.c: proc_reg_open |
| (GPL v2.0). */ |
| |
| /* Types. */ |
| |
| typedef unsigned char u8; |
| typedef _Bool bool; |
| typedef unsigned int gfp_t; |
| |
| struct file; |
| struct kmem_cache; |
| struct proc_dir_entry; |
| |
| struct inode { /* [...snip...] */ }; |
| |
| enum { |
| PROC_ENTRY_PERMANENT = 1U << 0, |
| }; |
| |
| struct proc_ops { |
| /* [...snip...] */ |
| int (*proc_open)(struct inode *, struct file *); |
| /* [...snip...] */ |
| int (*proc_release)(struct inode *, struct file *); |
| /* [...snip...] */ |
| }; |
| |
| struct proc_dir_entry { |
| /* [...snip...] */ |
| struct completion *pde_unload_completion; |
| /* [...snip...] */ |
| union { |
| const struct proc_ops *proc_ops; |
| const struct file_operations *proc_dir_ops; |
| }; |
| /* [...snip...] */ |
| u8 flags; |
| /* [...snip...] */ |
| }; |
| |
| struct pde_opener { |
| /* [...snip...] */ |
| struct file *file; |
| /* [...snip...] */ |
| }; |
| |
| struct proc_inode { |
| /* [...snip...] */ |
| struct proc_dir_entry *pde; |
| /* [...snip...] */ |
| struct inode vfs_inode; |
| }; |
| |
| /* Data. */ |
| |
| static struct kmem_cache *pde_opener_cache __attribute__((__section__(".data..ro_after_init"))); |
| |
| /* Functions. */ |
| |
| void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags) __attribute__((__malloc__)); |
| void kmem_cache_free(struct kmem_cache *, void *); |
| |
| static inline bool pde_is_permanent(const struct proc_dir_entry *pde) |
| { |
| return pde->flags & PROC_ENTRY_PERMANENT; |
| } |
| |
| static inline struct proc_inode *PROC_I(const struct inode *inode) |
| { |
| void *__mptr = (void *)(inode); |
| return ((struct proc_inode *)(__mptr - __builtin_offsetof(struct proc_inode, vfs_inode))); |
| } |
| |
| static inline struct proc_dir_entry *PDE(const struct inode *inode) |
| { |
| return PROC_I(inode)->pde; |
| } |
| |
| /* We don't want to emit bogus use of uninitialized value 'pdeo' |
| warnings from -Wanalyzer-use-of-uninitialized-value in this function; |
| these would require following infeasible paths in which "release" is |
| first NULL (to avoid the initialization of "pdeo") and then is non-NULL |
| (to access "pdeo"). |
| |
| "release" is sufficiently complicated in this function to hit the |
| complexity limit for symbolic values during enode exploration. */ |
| |
| static int proc_reg_open(struct inode *inode, struct file *file) |
| { |
| struct proc_dir_entry *pde = PDE(inode); |
| int rv = 0; |
| typeof(((struct proc_ops*)0)->proc_open) open; |
| typeof(((struct proc_ops*)0)->proc_release) release; |
| struct pde_opener *pdeo; |
| |
| if (pde_is_permanent(pde)) { |
| open = pde->proc_ops->proc_open; |
| if (open) |
| rv = open(inode, file); |
| return rv; |
| } |
| |
| /* [...snip...] */ |
| |
| release = pde->proc_ops->proc_release; |
| if (release) { |
| pdeo = kmem_cache_alloc(pde_opener_cache, |
| ((( gfp_t)(0x400u|0x800u)) |
| | (( gfp_t)0x40u) |
| | (( gfp_t)0x80u))); |
| if (!pdeo) { |
| rv = -12; |
| goto out_unuse; |
| } |
| } |
| |
| open = pde->proc_ops->proc_open; |
| if (open) |
| rv = open(inode, file); |
| |
| if (release) { |
| if (rv == 0) { |
| |
| pdeo->file = file; /* { dg-bogus "uninit" } */ |
| /* [...snip...] */ |
| } else |
| kmem_cache_free(pde_opener_cache, pdeo); /* { dg-bogus "uninit" } */ |
| } |
| |
| out_unuse: |
| /* [...snip...] */ |
| return rv; |
| } |