]>
Commit | Line | Data |
---|---|---|
288d6f25 SL |
1 | From a4a279248cfd0dc3f7fd0c9d28cdd438b6d23aa8 Mon Sep 17 00:00:00 2001 |
2 | From: Mike Kravetz <mike.kravetz@oracle.com> | |
3 | Date: Fri, 5 Apr 2019 18:39:06 -0700 | |
4 | Subject: hugetlbfs: fix memory leak for resv_map | |
5 | ||
6 | [ Upstream commit 58b6e5e8f1addd44583d61b0a03c0f5519527e35 ] | |
7 | ||
8 | When mknod is used to create a block special file in hugetlbfs, it will | |
9 | allocate an inode and kmalloc a 'struct resv_map' via resv_map_alloc(). | |
10 | inode->i_mapping->private_data will point the newly allocated resv_map. | |
11 | However, when the device special file is opened bd_acquire() will set | |
12 | inode->i_mapping to bd_inode->i_mapping. Thus the pointer to the | |
13 | allocated resv_map is lost and the structure is leaked. | |
14 | ||
15 | Programs to reproduce: | |
16 | mount -t hugetlbfs nodev hugetlbfs | |
17 | mknod hugetlbfs/dev b 0 0 | |
18 | exec 30<> hugetlbfs/dev | |
19 | umount hugetlbfs/ | |
20 | ||
21 | resv_map structures are only needed for inodes which can have associated | |
22 | page allocations. To fix the leak, only allocate resv_map for those | |
23 | inodes which could possibly be associated with page allocations. | |
24 | ||
25 | Link: http://lkml.kernel.org/r/20190401213101.16476-1-mike.kravetz@oracle.com | |
26 | Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com> | |
27 | Reviewed-by: Andrew Morton <akpm@linux-foundation.org> | |
28 | Reported-by: Yufen Yu <yuyufen@huawei.com> | |
29 | Suggested-by: Yufen Yu <yuyufen@huawei.com> | |
30 | Signed-off-by: Andrew Morton <akpm@linux-foundation.org> | |
31 | Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> | |
32 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
33 | --- | |
34 | fs/hugetlbfs/inode.c | 20 ++++++++++++++------ | |
35 | 1 file changed, 14 insertions(+), 6 deletions(-) | |
36 | ||
37 | diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c | |
38 | index cefae2350da5..27c4e2ac39a9 100644 | |
39 | --- a/fs/hugetlbfs/inode.c | |
40 | +++ b/fs/hugetlbfs/inode.c | |
41 | @@ -745,11 +745,17 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, | |
42 | umode_t mode, dev_t dev) | |
43 | { | |
44 | struct inode *inode; | |
45 | - struct resv_map *resv_map; | |
46 | + struct resv_map *resv_map = NULL; | |
47 | ||
48 | - resv_map = resv_map_alloc(); | |
49 | - if (!resv_map) | |
50 | - return NULL; | |
51 | + /* | |
52 | + * Reserve maps are only needed for inodes that can have associated | |
53 | + * page allocations. | |
54 | + */ | |
55 | + if (S_ISREG(mode) || S_ISLNK(mode)) { | |
56 | + resv_map = resv_map_alloc(); | |
57 | + if (!resv_map) | |
58 | + return NULL; | |
59 | + } | |
60 | ||
61 | inode = new_inode(sb); | |
62 | if (inode) { | |
63 | @@ -790,8 +796,10 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, | |
64 | break; | |
65 | } | |
66 | lockdep_annotate_inode_mutex_key(inode); | |
67 | - } else | |
68 | - kref_put(&resv_map->refs, resv_map_release); | |
69 | + } else { | |
70 | + if (resv_map) | |
71 | + kref_put(&resv_map->refs, resv_map_release); | |
72 | + } | |
73 | ||
74 | return inode; | |
75 | } | |
76 | -- | |
77 | 2.20.1 | |
78 |