| From dde1a87702117a783e5157251893a18997544113 Mon Sep 17 00:00:00 2001 |
| From: Mike Kravetz <mike.kravetz@oracle.com> |
| Date: Fri, 5 Apr 2019 18:39:06 -0700 |
| Subject: hugetlbfs: fix memory leak for resv_map |
| |
| [ Upstream commit 58b6e5e8f1addd44583d61b0a03c0f5519527e35 ] |
| |
| When mknod is used to create a block special file in hugetlbfs, it will |
| allocate an inode and kmalloc a 'struct resv_map' via resv_map_alloc(). |
| inode->i_mapping->private_data will point the newly allocated resv_map. |
| However, when the device special file is opened bd_acquire() will set |
| inode->i_mapping to bd_inode->i_mapping. Thus the pointer to the |
| allocated resv_map is lost and the structure is leaked. |
| |
| Programs to reproduce: |
| mount -t hugetlbfs nodev hugetlbfs |
| mknod hugetlbfs/dev b 0 0 |
| exec 30<> hugetlbfs/dev |
| umount hugetlbfs/ |
| |
| resv_map structures are only needed for inodes which can have associated |
| page allocations. To fix the leak, only allocate resv_map for those |
| inodes which could possibly be associated with page allocations. |
| |
| Link: http://lkml.kernel.org/r/20190401213101.16476-1-mike.kravetz@oracle.com |
| Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com> |
| Reviewed-by: Andrew Morton <akpm@linux-foundation.org> |
| Reported-by: Yufen Yu <yuyufen@huawei.com> |
| Suggested-by: Yufen Yu <yuyufen@huawei.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin (Microsoft) <sashal@kernel.org> |
| --- |
| fs/hugetlbfs/inode.c | 20 ++++++++++++++------ |
| 1 file changed, 14 insertions(+), 6 deletions(-) |
| |
| diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c |
| index a7fa037b876b..a3a3d256fb0e 100644 |
| --- a/fs/hugetlbfs/inode.c |
| +++ b/fs/hugetlbfs/inode.c |
| @@ -741,11 +741,17 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, |
| umode_t mode, dev_t dev) |
| { |
| struct inode *inode; |
| - struct resv_map *resv_map; |
| + struct resv_map *resv_map = NULL; |
| |
| - resv_map = resv_map_alloc(); |
| - if (!resv_map) |
| - return NULL; |
| + /* |
| + * Reserve maps are only needed for inodes that can have associated |
| + * page allocations. |
| + */ |
| + if (S_ISREG(mode) || S_ISLNK(mode)) { |
| + resv_map = resv_map_alloc(); |
| + if (!resv_map) |
| + return NULL; |
| + } |
| |
| inode = new_inode(sb); |
| if (inode) { |
| @@ -780,8 +786,10 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, |
| break; |
| } |
| lockdep_annotate_inode_mutex_key(inode); |
| - } else |
| - kref_put(&resv_map->refs, resv_map_release); |
| + } else { |
| + if (resv_map) |
| + kref_put(&resv_map->refs, resv_map_release); |
| + } |
| |
| return inode; |
| } |
| -- |
| 2.20.1 |
| |