Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix potential memory leak in vinput #279

Merged
merged 1 commit into from
Oct 26, 2024
Merged

Conversation

NOVBobLee
Copy link
Collaborator

In the export_store function, the error handling paths followed a successful vinput_alloc_vdevice call are missing a corresponding input_free_device call. Since vinput_alloc_vdevice internally calls input_allocate_device, and input_register_device has not been called yet, input_free_device should be used to properly free the allocated input_device struct in this scenario[1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/input/input.c#n2094

In the export_store function, the error handling paths followed a
successful vinput_alloc_vdevice call are missing a corresponding
input_free_device call. Since vinput_alloc_vdevice internally calls
input_allocate_device, and input_register_device has not been called
yet, input_free_device should be used to properly free the allocated
input_device struct in this scenario[1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/input/input.c#n2094
@jserv jserv requested a review from linD026 October 25, 2024 05:25
@jserv
Copy link
Contributor

jserv commented Oct 25, 2024

Can you make use of eBPF to locate memory errors?

@NOVBobLee
Copy link
Collaborator Author

I never used eBPF before, I'll try it at night.

@jserv
Copy link
Contributor

jserv commented Oct 25, 2024

I never used eBPF before, I'll try it at night.

kmemleak is really powerful, and you should try it.

@NOVBobLee
Copy link
Collaborator Author

NOVBobLee commented Oct 25, 2024

kmemleak is really powerful, and you should try it.

Great! Since the original testing process involves the following steps:

1. insmod vinput.ko
2. insmod vkbd.ko
3. echo vkbd > /sys/class/vinput/export ( <- memory leak point)

To use the kmodleak tool effectively, I will combine these modules into a single one for testing.

Update:

I misunderstood how to use kmodleak. It only takes the module name as the tracing target and doesn't actively load or unload modules.

@NOVBobLee
Copy link
Collaborator Author

Here's the outputs from kmodleak.

(original code)

using page size: 4096
Tracing module memory allocs... Unload module (or hit Ctrl-C) to end
module 'vinput' loaded
module 'vinput' unloaded

8 stacks with outstanding allocations:
2048 bytes in 1 allocations from stack
	addr = 0xffff8d77c971e800 size = 2048
	  0 [<ffffffffa07b8ee9>] __kmalloc_cache_noprof+0x1f9
	  1 [<ffffffffa07b8ee9>] __kmalloc_cache_noprof+0x1f9
	  2 [<ffffffffa0ea6b51>] input_allocate_device+0x21
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- here
	  3 [<ffffffffc085e6ee>] export_store+0x16e
	  4 [<ffffffffa090c521>] kernfs_fop_write_iter+0x141
	  5 [<ffffffffa083a074>] vfs_write+0x294
	  6 [<ffffffffa083a5cd>] ksys_write+0x6d
	  7 [<ffffffffa1260982>] do_syscall_64+0x82
	  8 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
136 bytes in 1 allocations from stack
	addr = 0xffff8d77eac9b990 size = 136
	  0 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  1 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  2 [<ffffffffa0909c0b>] __kernfs_new_node+0x5b
	  3 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  4 [<ffffffffa090d389>] __kernfs_create_file+0x29
	  5 [<ffffffffa090e372>] sysfs_add_file_mode_ns+0x72
	  6 [<ffffffffa090f4b0>] internal_create_group+0x200
	  7 [<ffffffffa090f7d2>] internal_create_groups+0x42
	  8 [<ffffffffa0d8f883>] class_register+0x103
	  9 [<ffffffffc0893074>] vinput_init+0x64
	 10 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 11 [<ffffffffa058c350>] do_init_module+0x60
	 12 [<ffffffffa058e829>] init_module_from_file+0x89
	 13 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 14 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 15 [<ffffffffa1260982>] do_syscall_64+0x82
	 16 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
136 bytes in 1 allocations from stack
	addr = 0xffff8d77eac9bbb0 size = 136
	  0 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  1 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  2 [<ffffffffa0909c0b>] __kernfs_new_node+0x5b
	  3 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  4 [<ffffffffa090b56b>] kernfs_create_dir_ns+0x2b
	  5 [<ffffffffa090eac4>] sysfs_create_dir_ns+0x84
	  6 [<ffffffffa11c448e>] kobject_add_internal+0xbe
	  7 [<ffffffffa11c469e>] kset_register+0x5e
	  8 [<ffffffffa0d8f871>] class_register+0xf1
	  9 [<ffffffffc0893074>] vinput_init+0x64
	 10 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 11 [<ffffffffa058c350>] do_init_module+0x60
	 12 [<ffffffffa058e829>] init_module_from_file+0x89
	 13 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 14 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 15 [<ffffffffa1260982>] do_syscall_64+0x82
	 16 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
96 bytes in 1 allocations from stack
	addr = 0xffff8d77cc7328a0 size = 96
	  0 [<ffffffffa07b8ee9>] __kmalloc_cache_noprof+0x1f9
	  1 [<ffffffffa07b8ee9>] __kmalloc_cache_noprof+0x1f9
	  2 [<ffffffffa0ea6b7d>] input_allocate_device+0x4d
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- here
	  3 [<ffffffffc085e6ee>] export_store+0x16e
	  4 [<ffffffffa090c521>] kernfs_fop_write_iter+0x141
	  5 [<ffffffffa083a074>] vfs_write+0x294
	  6 [<ffffffffa083a5cd>] ksys_write+0x6d
	  7 [<ffffffffa1260982>] do_syscall_64+0x82
	  8 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
8 bytes in 1 allocations from stack
	addr = 0xffff8d77c3411b28 size = 8
	  0 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  1 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  2 [<ffffffffa074ba28>] kstrdup+0x38
	  3 [<ffffffffa0909bee>] __kernfs_new_node+0x3e
	  4 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  5 [<ffffffffa090d389>] __kernfs_create_file+0x29
	  6 [<ffffffffa090e372>] sysfs_add_file_mode_ns+0x72
	  7 [<ffffffffa090f4b0>] internal_create_group+0x200
	  8 [<ffffffffa090f7d2>] internal_create_groups+0x42
	  9 [<ffffffffa0d8f883>] class_register+0x103
	 10 [<ffffffffc0893074>] vinput_init+0x64
	 11 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 12 [<ffffffffa058c350>] do_init_module+0x60
	 13 [<ffffffffa058e829>] init_module_from_file+0x89
	 14 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 15 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 16 [<ffffffffa1260982>] do_syscall_64+0x82
	 17 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
8 bytes in 1 allocations from stack
	addr = 0xffff8d77c595b960 size = 8
	  0 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  1 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  2 [<ffffffffa0a8cc9b>] kvasprintf+0x6b
	  3 [<ffffffffa11c4a60>] kobject_set_name_vargs+0x20
	  4 [<ffffffffa0d8443d>] dev_set_name+0x5d
	  5 [<ffffffffa0ea6c37>] input_allocate_device+0x107
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- here
	  6 [<ffffffffc085e6ee>] export_store+0x16e
	  7 [<ffffffffa090c521>] kernfs_fop_write_iter+0x141
	  8 [<ffffffffa083a074>] vfs_write+0x294
	  9 [<ffffffffa083a5cd>] ksys_write+0x6d
	 10 [<ffffffffa1260982>] do_syscall_64+0x82
	 11 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
8 bytes in 1 allocations from stack
	addr = 0xffff8d77c595b5f0 size = 8
	  0 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  1 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  2 [<ffffffffa0a8cc9b>] kvasprintf+0x6b
	  3 [<ffffffffa11c4a60>] kobject_set_name_vargs+0x20
	  4 [<ffffffffa0d8443d>] dev_set_name+0x5d
	  5 [<ffffffffc085e738>] export_store+0x1b8
	  6 [<ffffffffa090c521>] kernfs_fop_write_iter+0x141
	  7 [<ffffffffa083a074>] vfs_write+0x294
	  8 [<ffffffffa083a5cd>] ksys_write+0x6d
	  9 [<ffffffffa1260982>] do_syscall_64+0x82
	 10 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
8 bytes in 1 allocations from stack
	addr = 0xffff8d77c3411388 size = 8
	  0 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  1 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  2 [<ffffffffa074ba28>] kstrdup+0x38
	  3 [<ffffffffa0909bee>] __kernfs_new_node+0x3e
	  4 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  5 [<ffffffffa090b56b>] kernfs_create_dir_ns+0x2b
	  6 [<ffffffffa090eac4>] sysfs_create_dir_ns+0x84
	  7 [<ffffffffa11c448e>] kobject_add_internal+0xbe
	  8 [<ffffffffa11c469e>] kset_register+0x5e
	  9 [<ffffffffa0d8f871>] class_register+0xf1
	 10 [<ffffffffc0893074>] vinput_init+0x64
	 11 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 12 [<ffffffffa058c350>] do_init_module+0x60
	 13 [<ffffffffa058e829>] init_module_from_file+0x89
	 14 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 15 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 16 [<ffffffffa1260982>] do_syscall_64+0x82
	 17 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
done

(patched code)

using page size: 4096
Tracing module memory allocs... Unload module (or hit Ctrl-C) to end
module 'vinput' loaded
module 'vinput' unloaded

6 stacks with outstanding allocations:
584 bytes in 1 allocations from stack
	addr = 0xffff8d77ccd43478 size = 584
	  0 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  1 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  2 [<ffffffffa11d7c06>] radix_tree_node_alloc.constprop.0+0x86
	  3 [<ffffffffa11d92a7>] idr_get_free+0x237
	  4 [<ffffffffa11c2335>] idr_alloc_u32+0x75
	  5 [<ffffffffa11c248b>] idr_alloc_cyclic+0x5b
	  6 [<ffffffffa0909c4e>] __kernfs_new_node+0x9e
	  7 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  8 [<ffffffffa090b56b>] kernfs_create_dir_ns+0x2b
	  9 [<ffffffffa090eac4>] sysfs_create_dir_ns+0x84
	 10 [<ffffffffa11c448e>] kobject_add_internal+0xbe
	 11 [<ffffffffa11c469e>] kset_register+0x5e
	 12 [<ffffffffa0d8f871>] class_register+0xf1
	 13 [<ffffffffc0893074>] vinput_init+0x64
	 14 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 15 [<ffffffffa058c350>] do_init_module+0x60
	 16 [<ffffffffa058e829>] init_module_from_file+0x89
	 17 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 18 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 19 [<ffffffffa1260982>] do_syscall_64+0x82
	 20 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
136 bytes in 1 allocations from stack
	addr = 0xffff8d77eac9caa0 size = 136
	  0 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  1 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  2 [<ffffffffa0909c0b>] __kernfs_new_node+0x5b
	  3 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  4 [<ffffffffa090b56b>] kernfs_create_dir_ns+0x2b
	  5 [<ffffffffa090eac4>] sysfs_create_dir_ns+0x84
	  6 [<ffffffffa11c448e>] kobject_add_internal+0xbe
	  7 [<ffffffffa11c469e>] kset_register+0x5e
	  8 [<ffffffffa0d8f871>] class_register+0xf1
	  9 [<ffffffffc0893074>] vinput_init+0x64
	 10 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 11 [<ffffffffa058c350>] do_init_module+0x60
	 12 [<ffffffffa058e829>] init_module_from_file+0x89
	 13 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 14 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 15 [<ffffffffa1260982>] do_syscall_64+0x82
	 16 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
136 bytes in 1 allocations from stack
	addr = 0xffff8d77eac9c4c8 size = 136
	  0 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  1 [<ffffffffa07b8718>] kmem_cache_alloc_noprof+0x1e8
	  2 [<ffffffffa0909c0b>] __kernfs_new_node+0x5b
	  3 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  4 [<ffffffffa090d389>] __kernfs_create_file+0x29
	  5 [<ffffffffa090e372>] sysfs_add_file_mode_ns+0x72
	  6 [<ffffffffa090f4b0>] internal_create_group+0x200
	  7 [<ffffffffa090f7d2>] internal_create_groups+0x42
	  8 [<ffffffffa0d8f883>] class_register+0x103
	  9 [<ffffffffc0893074>] vinput_init+0x64
	 10 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 11 [<ffffffffa058c350>] do_init_module+0x60
	 12 [<ffffffffa058e829>] init_module_from_file+0x89
	 13 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 14 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 15 [<ffffffffa1260982>] do_syscall_64+0x82
	 16 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
8 bytes in 1 allocations from stack
	addr = 0xffff8d77c3169ac0 size = 8
	  0 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  1 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  2 [<ffffffffa0a8cc9b>] kvasprintf+0x6b
	  3 [<ffffffffa11c4a60>] kobject_set_name_vargs+0x20
	  4 [<ffffffffa0d8443d>] dev_set_name+0x5d
	  5 [<ffffffffc085e738>] export_store+0x1b8
	  6 [<ffffffffa090c521>] kernfs_fop_write_iter+0x141
	  7 [<ffffffffa083a074>] vfs_write+0x294
	  8 [<ffffffffa083a5cd>] ksys_write+0x6d
	  9 [<ffffffffa1260982>] do_syscall_64+0x82
	 10 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
8 bytes in 1 allocations from stack
	addr = 0xffff8d77c3411700 size = 8
	  0 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  1 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  2 [<ffffffffa074ba28>] kstrdup+0x38
	  3 [<ffffffffa0909bee>] __kernfs_new_node+0x3e
	  4 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  5 [<ffffffffa090b56b>] kernfs_create_dir_ns+0x2b
	  6 [<ffffffffa090eac4>] sysfs_create_dir_ns+0x84
	  7 [<ffffffffa11c448e>] kobject_add_internal+0xbe
	  8 [<ffffffffa11c469e>] kset_register+0x5e
	  9 [<ffffffffa0d8f871>] class_register+0xf1
	 10 [<ffffffffc0893074>] vinput_init+0x64
	 11 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 12 [<ffffffffa058c350>] do_init_module+0x60
	 13 [<ffffffffa058e829>] init_module_from_file+0x89
	 14 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 15 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 16 [<ffffffffa1260982>] do_syscall_64+0x82
	 17 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
8 bytes in 1 allocations from stack
	addr = 0xffff8d77c34110c8 size = 8
	  0 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  1 [<ffffffffa07b7c27>] __kmalloc_node_track_caller_noprof+0x257
	  2 [<ffffffffa074ba28>] kstrdup+0x38
	  3 [<ffffffffa0909bee>] __kernfs_new_node+0x3e
	  4 [<ffffffffa090b0a6>] kernfs_new_node+0x56
	  5 [<ffffffffa090d389>] __kernfs_create_file+0x29
	  6 [<ffffffffa090e372>] sysfs_add_file_mode_ns+0x72
	  7 [<ffffffffa090f4b0>] internal_create_group+0x200
	  8 [<ffffffffa090f7d2>] internal_create_groups+0x42
	  9 [<ffffffffa0d8f883>] class_register+0x103
	 10 [<ffffffffc0893074>] vinput_init+0x64
	 11 [<ffffffffa0402a8b>] do_one_initcall+0x5b
	 12 [<ffffffffa058c350>] do_init_module+0x60
	 13 [<ffffffffa058e829>] init_module_from_file+0x89
	 14 [<ffffffffa058e9b1>] idempotent_init_module+0x121
	 15 [<ffffffffa058ecde>] __x64_sys_finit_module+0x5e
	 16 [<ffffffffa1260982>] do_syscall_64+0x82
	 17 [<ffffffffa140012f>] entry_SYSCALL_64_after_hwframe+0x76
done

@@ -283,10 +283,12 @@ static ssize_t export_store(struct class *class, struct class_attribute *attr,
return len;

fail_register_vinput:
input_free_device(vinput->input);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we call input_allocate_device in vinput_alloc_vdevice , why do we handle it manually instead of putting input_free_device in the corresponding release function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe input_free_device should be decoupled from the release function vinput_destroy_vdevice. In both unexport_store and vinput_unregister, the vinput_unregister_vdevice function is called before device_unregister, with each invoking input_unregister_device followed by vinput_destroy_vdevice. Embedding input_free_device within vinput_destroy_vdevice could contradict the documented comments in the source code [1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/input/input.c#n2386

Copy link
Collaborator

@linD026 linD026 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks for letting me know this.

@jserv
Copy link
Contributor

jserv commented Oct 26, 2024

I misunderstood how to use kmodleak. It only takes the module name as the tracing target and doesn't actively load or unload modules.

We can even automate the use of kmodleak, enforcing the checks for all loadable Linux modules in CI pipeline.

@jserv jserv merged commit e3e2ee3 into sysprog21:master Oct 26, 2024
1 check passed
@NOVBobLee NOVBobLee deleted the fix_memleak branch October 26, 2024 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants