操作系统版本:centos7.3
内核版本:3.10.0-693
利用slabtop命令,发现有大量的kmalloc-64占用,且一直未释放,初步怀疑是内存泄漏,但不知道谁泄漏的。
通过打开kmemleak,发现有install进程大量__kmalloc且size大小刚好也为64k
(线上机器会定期调用/usr/bin/install命令,至于为何调用,那是业务的需求了。。。。。。写个死循环疯狂调用install确实能复现,该命令实际是调用了setxattr函数,你可以写个c函数疯狂调用之复现)
函数栈如下:
unreferenced object 0xffff8801c5a40000 (size 64):
comm "install", pid 15131, jiffies 4300179882 (age 217981.193s)
hex dump (first 32 bytes):
01 00 00 00 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b ....kkkkkkkkkkkk
03 00 00 00 01 00 06 00 6b 6b 6b 6b 04 00 00 00 ........kkkk....
backtrace:
[<ffffffff816a2a1e>] kmemleak_alloc+0x4e/0xb0
[<ffffffff811e313d>] __kmalloc+0xdd/0x1b0
[<ffffffff812692dc>] posix_acl_alloc+0x1c/0x30
[<ffffffff81269c02>] posix_acl_from_xattr+0x82/0x190
[<ffffffffc0248eb2>] ext4_xattr_set_acl+0x92/0x2d0 [ext4]
[<ffffffff8122d518>] generic_setxattr+0x68/0x80
[<ffffffff8122dd85>] __vfs_setxattr_noperm+0x65/0x1b0
[<ffffffff8122df85>] vfs_setxattr+0xb5/0xc0
[<ffffffff8122e0be>] setxattr+0x12e/0x1c0
[<ffffffff8122e5be>] SyS_fsetxattr+0xce/0x110
[<ffffffff816c2695>] system_call_fastpath+0x1c/0x21
[<ffffffffffffffff>] 0xffffffffffffffff
但是还是不明白为何会泄漏,没办法,只能上gdb大法。。。
用gdb跟踪内核,成功发现泄露点位于函数:ext4_xattr_set_acl
该函数中其中一行: error = posix_acl_update_mode(inode, &mode, &acl);
继续跟进 posix_acl_update_mode函数,其中有一行:*acl = NULL,该操作会将ext4_xattr_set_acl中的acl置空;ext4_xattr_set_acl中最后部分有一行: posix_acl_release(acl); 该函数由于判断acl为NULL,导致无法执行到kfree,最终引起不断创建的acl大量堆积,内存泄露。
附上本人的patch修复:
— a/fs/ext4/acl.c 2018-04-29 01:23:50.000000000 +0800
+++ b/fs/ext4/acl.c 2019-09-17 17:48:03.094206145 +0800
@@ -404,6 +404,7 @@ ext4_xattr_set_acl(struct dentry *dentry
struct inode *inode = dentry->d_inode;
handle_t *handle;
struct posix_acl *acl;
+ struct posix_acl *p = NULL;
int error, retries = 0;
int update_mode = 0;
umode_t mode = inode->i_mode;
@@ -426,6 +427,7 @@ ext4_xattr_set_acl(struct dentry *dentry
}
} else
acl = NULL;
+ p = acl;retry:
handle = ext4_journal_start(inode, EXT4_HT_XATTR,
@@ -452,7 +454,7 @@ out_stop:
goto retry;release_and_out:
- posix_acl_release(acl);
+ posix_acl_release(p);
return error;
}
打上补丁,问题成功解决。