[v5] kobject: Fix global-out-of-bounds in kobject_action_type()

Message ID 20230516123719.117137-1-xiafukun@huawei.com
State New
Headers
Series [v5] kobject: Fix global-out-of-bounds in kobject_action_type() |

Commit Message

Xia Fukun May 16, 2023, 12:37 p.m. UTC
  The following c language code can trigger KASAN's global variable
out-of-bounds access error in kobject_action_type():

int main() {
    int fd;
    char *filename = "/sys/block/ram12/uevent";
    char str[86] = "offline";
    int len = 86;

    fd = open(filename, O_WRONLY);
    if (fd == -1) {
        printf("open");
        exit(1);
    }

    if (write(fd, str, len) == -1) {
        printf("write");
        exit(1);
    }

    close(fd);
    return 0;
}

Function kobject_action_type() receives the input parameters buf and count,
where count is the length of the string buf.

In the use case we provided, count is 86, the count_first is 85.
Buf points to a string with a length of 86, and its first seven
characters are "offline".
In line 87 of the code, kobject_actions[action] is the string "offline"
with the length of 7,an out-of-boundary access will appear:

kobject_actions[action][85].

Use sysfs_match_string() to replace the fragile and convoluted loop.
This function is well-tested for parsing sysfs inputs. Moreover, this
modification will not cause any functional changes.

Fixes: f36776fafbaa ("kobject: support passing in variables for synthetic uevents")
Signed-off-by: Xia Fukun <xiafukun@huawei.com>
---
v4 -> v5:
- Fixed build errors and warnings, and retested the patch.

v3 -> v4:
- Refactor the function to be more obviously correct and readable.
---
 lib/kobject_uevent.c | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)
  

Comments

Peter Rajnoha May 16, 2023, 2:02 p.m. UTC | #1
On 5/16/23 14:37, Xia Fukun wrote:
> The following c language code can trigger KASAN's global variable
> out-of-bounds access error in kobject_action_type():
> 
> int main() {
>     int fd;
>     char *filename = "/sys/block/ram12/uevent";
>     char str[86] = "offline";
>     int len = 86;
> 
>     fd = open(filename, O_WRONLY);
>     if (fd == -1) {
>         printf("open");
>         exit(1);
>     }
> 
>     if (write(fd, str, len) == -1) {
>         printf("write");
>         exit(1);
>     }
> 
>     close(fd);
>     return 0;
> }
> 
> Function kobject_action_type() receives the input parameters buf and count,
> where count is the length of the string buf.
> 
> In the use case we provided, count is 86, the count_first is 85.
> Buf points to a string with a length of 86, and its first seven
> characters are "offline".
> In line 87 of the code, kobject_actions[action] is the string "offline"
> with the length of 7,an out-of-boundary access will appear:
> 
> kobject_actions[action][85].
> 
> Use sysfs_match_string() to replace the fragile and convoluted loop.
> This function is well-tested for parsing sysfs inputs. Moreover, this
> modification will not cause any functional changes.
> 
> Fixes: f36776fafbaa ("kobject: support passing in variables for synthetic uevents")
> Signed-off-by: Xia Fukun <xiafukun@huawei.com>
> ---
> v4 -> v5:
> - Fixed build errors and warnings, and retested the patch.
> 

Please, also check this is still working:
https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent

When I try passing the example line "add
fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed A=1 B=abc", it doesn't recognize
such input anymore and it incorrectly considers it as erroneous.
  
Xia Fukun May 17, 2023, 8:54 a.m. UTC | #2
在 2023/5/16 22:02, Peter Rajnoha wrote:
> On 5/16/23 14:37, Xia Fukun wrote:

>> ---
>> v4 -> v5:
>> - Fixed build errors and warnings, and retested the patch.
>>
> 
> Please, also check this is still working:
> https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent
> 
> When I try passing the example line "add
> fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed A=1 B=abc", it doesn't recognize
> such input anymore and it incorrectly considers it as erroneous.
> 

Why did I receive the following error message when passing the example
line "add fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed A=1 B=abc" using the
original mainline code?

synth uevent: /devices/virtual/block/ram12: incorrect uevent action arguments
block ram12: uevent: failed to send synthetic uevent: -22

Is there a problem with my test case, or is the original code unable
to successfully parse the sample?
  
Peter Rajnoha May 17, 2023, 9:07 a.m. UTC | #3
On 5/17/23 10:54, Xia Fukun wrote:
> 在 2023/5/16 22:02, Peter Rajnoha wrote:
>> On 5/16/23 14:37, Xia Fukun wrote:
> 
>>> ---
>>> v4 -> v5:
>>> - Fixed build errors and warnings, and retested the patch.
>>>
>>
>> Please, also check this is still working:
>> https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent
>>
>> When I try passing the example line "add
>> fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed A=1 B=abc", it doesn't recognize
>> such input anymore and it incorrectly considers it as erroneous.
>>
> 
> Why did I receive the following error message when passing the example
> line "add fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed A=1 B=abc" using the
> original mainline code?
> 
> synth uevent: /devices/virtual/block/ram12: incorrect uevent action arguments
> block ram12: uevent: failed to send synthetic uevent: -22
> 
> Is there a problem with my test case, or is the original code unable
> to successfully parse the sample?
> 

That works for me, I'm testing on kernel 6.2.15:

# echo "add fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed A=1 B=abc" >
/sys/block/ram0/uevent

# udevadm monitor --kernel --env
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[189.376386] add      /devices/virtual/block/ram0 (block)
ACTION=add
DEVPATH=/devices/virtual/block/ram0
SUBSYSTEM=block
SYNTH_UUID=fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed
SYNTH_ARG_A=1
SYNTH_ARG_B=abc
DEVNAME=/dev/ram0
DEVTYPE=disk
DISKSEQ=14
SEQNUM=3781
MAJOR=1
MINOR=0
  

Patch

diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 7c44b7ae4c5c..1ec20a7d3d45 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -64,9 +64,8 @@  static int kobject_action_type(const char *buf, size_t count,
 			       const char **args)
 {
 	enum kobject_action action;
-	size_t count_first;
 	const char *args_start;
-	int ret = -EINVAL;
+	int i, ret = -EINVAL;
 
 	if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
 		count--;
@@ -75,23 +74,22 @@  static int kobject_action_type(const char *buf, size_t count,
 		goto out;
 
 	args_start = strnchr(buf, count, ' ');
-	if (args_start) {
-		count_first = args_start - buf;
+	if (args_start)
 		args_start = args_start + 1;
-	} else
-		count_first = count;
 
-	for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
-		if (strncmp(kobject_actions[action], buf, count_first) != 0)
-			continue;
-		if (kobject_actions[action][count_first] != '\0')
-			continue;
-		if (args)
-			*args = args_start;
-		*type = action;
-		ret = 0;
-		break;
-	}
+	/* Use sysfs_match_string() to replace the fragile and convoluted loop */
+	i = sysfs_match_string(kobject_actions, buf);
+
+	if (i < 0)
+		return ret;
+
+	action = i;
+
+	if (args)
+		*args = args_start;
+
+	*type = action;
+	ret = 0;
 out:
 	return ret;
 }