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

stack-buffer-overflow at evutil_parse_sockaddr_port #1573

Closed
xiaoge1001 opened this issue Mar 14, 2024 · 15 comments
Closed

stack-buffer-overflow at evutil_parse_sockaddr_port #1573

xiaoge1001 opened this issue Mar 14, 2024 · 15 comments

Comments

@xiaoge1001
Copy link

I used the fuzz testing tool and found that libevent has a stack-buffer-overflow problem. The error code segment is where memcpy is called.This is a bug?
https://github.com/libevent/libevent/blob/release-2.1.12-stable/evutil.c#L2220

@liudongmiao
Copy link
Contributor

@xiaoge1001 Did you make sure you have the size for the out? You should make sure the output is large enough for ipv6, like struct sockaddr_storage or so.

@xiaoge1001
Copy link
Author

xiaoge1001 commented Mar 16, 2024

@xiaoge1001 Did you make sure you have the size for the out? You should make sure the output is large enough for ipv6, like struct sockaddr_storage or so.

Hi, is this saying about the value of *outlen?

@liudongmiao
Copy link
Contributor

No, it's about the memcpy, check the manual of memcpy. Normally, struct sockaddr_storage should be enough.

@xiaoge1001
Copy link
Author

xiaoge1001 commented Mar 16, 2024

struct sockaddr_storage {
	union {
		struct sockaddr ss_sa;
		struct sockaddr_in ss_sin;
		struct sockaddr_in6 ss_sin6;
		char ss_padding[128];
	} ss_union;
};

struct sockaddr *out;
struct sockaddr_in6 sin6;
... ....
memcpy(out, &sin6, sizeof(sin6));

Sorry, I don't understand how this is directly related to the struct sockaddr_storage structure.

@xiaoge1001
Copy link
Author

xiaoge1001 commented Mar 19, 2024

I tested the 2.1.12 version and the latest version on GitHub, both have the problem. The gdb debugging result is as follows:

Breakpoint 1, evutil_parse_sockaddr_port (ip_as_string=<optimized out>, out=<optimized out>, outlen=<optimized out>) at /root/build-libevent/src/libevent/evutil.c:2383
2383    in /root/build-libevent/src/libevent/evutil.c
(gdb) p sizeof(out)
$4 = 8
(gdb) p sizeof(sin6)
$5 = 28
(gdb) p sizeof(*out)
$6 = 16
(gdb) ptype sin6
type = struct sockaddr_in6 {
    sa_family_t sin6_family;
    in_port_t sin6_port;
    uint32_t sin6_flowinfo;
    struct in6_addr sin6_addr;
    uint32_t sin6_scope_id;
}
(gdb) ptype out
type = struct sockaddr {
    sa_family_t sa_family;
    char sa_data[14];
} *
(gdb)

@liudongmiao
Copy link
Contributor

liudongmiao commented Mar 20, 2024

@xiaoge1001 Could you show a poc? How to reproduce.

		if ((int)sizeof(sin6) > *outlen)
			return -1;
                // ignore other codes
		memcpy(out, &sin6, sizeof(sin6));

As it never copy more data to out, so it's safe. However, if you cannot make sure out has outlen length, then there should be problem.

@xiaoge1001
Copy link
Author

https://github.com/libevent/libevent/blob/master/evutil.c#L2379
I add the code:

printf("outlen is %d\n", *outlen);

out:
outlen is 2051025536

@xiaoge1001
Copy link
Author

@xiaoge1001 Could you show a poc? How to reproduce.

		if ((int)sizeof(sin6) > *outlen)
			return -1;
                // ignore other codes
		memcpy(out, &sin6, sizeof(sin6));

As it never copy more data to out, so it's safe. However, if you cannot make sure out has outlen length, then there should be problem.

cat crash-sanitizer-145282c4e12d06cafbc46ffe6c9f0f25e19e34da23782953cd2ccc08a535f826
1::0000

@liudongmiao
Copy link
Contributor

@xiaoge1001 As I'm not familiar with crash-sanitizer or other ... cloud you change

		if ((int)sizeof(sin6) > *outlen)
			return -1;
		sin6.sin6_scope_id = if_index;
		memset(out, 0, *outlen);
		memcpy(out, &sin6, sizeof(sin6));

to

		if ((int)sizeof(sin6) > *outlen)
			return -1;
		sin6.sin6_scope_id = if_index;
		memset(out, 0, sizeof(sin6));
		memcpy(out, &sin6, sizeof(sin6));

or just remove the memset? And, actually, I don't believe out have size of 2051025536.

@xiaoge1001
Copy link
Author

memset(out, 0, *outlen); change to memset(out, 0, sizeof(sin6));

the problem doesn't recur. Why is that?

@xiaoge1001
Copy link
Author

@xiaoge1001 As I'm not familiar with crash-sanitizer or other ... cloud you change

		if ((int)sizeof(sin6) > *outlen)
			return -1;
		sin6.sin6_scope_id = if_index;
		memset(out, 0, *outlen);
		memcpy(out, &sin6, sizeof(sin6));

to

		if ((int)sizeof(sin6) > *outlen)
			return -1;
		sin6.sin6_scope_id = if_index;
		memset(out, 0, sizeof(sin6));
		memcpy(out, &sin6, sizeof(sin6));

or just remove the memset? And, actually, I don't believe out have size of 2051025536.

This is carefully constructed problematic input to test.

@liudongmiao
Copy link
Contributor

memset(out, 0, *outlen); change to memset(out, 0, sizeof(sin6));

the problem doesn't recur. Why is that?

As the outlen value is invalid, then memset with a large value, it will set all the memory to zero.
In your example, 2051025536 is about 2G, I think this would be large enough to make your system down.

However, I thinks it's a bug in your codes, not evutil_parse_sockaddr_port, libevent may choose to fix or not.

@xiaoge1001
Copy link
Author

memset(out, 0, *outlen); change to memset(out, 0, sizeof(sin6));
the problem doesn't recur. Why is that?

As the outlen value is invalid, then memset with a large value, it will set all the memory to zero. In your example, 2051025536 is about 2G, I think this would be large enough to make your system down.

However, I thinks it's a bug in your codes, not evutil_parse_sockaddr_port, libevent may choose to fix or not.

Can we add a parameter check? For example:
a6c3d37

liudongmiao pushed a commit to brevent/libevent that referenced this issue Mar 20, 2024
In `evutil_parse_sockaddr_port`, it whould memset the out to zero, however, if
`outlen` is invalid, it would be an undefined behavior.

This should close libevent#1573.
@azat
Copy link
Member

azat commented Mar 29, 2024

Can we add a parameter check? For example:

This looks like a hack, there is a check for outlen and it should be enough -

libevent/evutil.c

Line 2379 in 4fd07f0

if ((int)sizeof(sin6) > *outlen)

To catch other errors one could use sanitizers.

@azat azat closed this as completed Mar 29, 2024
@azat
Copy link
Member

azat commented Mar 29, 2024

If you have some strong constructive objections, feel free to reopen.

liudongmiao added a commit to brevent/libevent that referenced this issue Mar 30, 2024
In `evutil_parse_sockaddr_port`, it would `memset` the `out` to zero,
however, the `memset` is unnecessary before `memcpy`, and may cause
undefined behavior if the `outlen` is invalid.

This should close libevent#1573.
azat pushed a commit that referenced this issue Mar 31, 2024
In `evutil_parse_sockaddr_port`, it would `memset` the `out` to zero,
however, the `memset` is unnecessary before `memcpy`, and may cause
undefined behavior if the `outlen` is invalid.

This should close #1573.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants