CVE-2022-48620: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') (CWE-120)
value: uev (aka libuev) before 2.4.1 has a buffer overflow in epoll_wait if maxevents is a large number.
CWE description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
CVSS v3 base score: 9.8
Published: 2024-01-12T04:15Z
Repository: https://github.com/troglobit/libuev
Language: C
Files changed: src/uev.c
Commit date: 2022-12-17 15:02:21 +0100
Commit message: Fix #27: possible buffer overrun in uev_run()

If uev_init1() is called with maxevents > 10 the call to epoll_wait()
might cause buffer overflow.  Reported by Steve Palmer.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>

Diff: diff --git a/src/uev.c b/src/uev.c
index 10d025f..548866a 100644
--- a/src/uev.c
+++ b/src/uev.c
@@ -194,15 +194,15 @@ int uev_init(uev_ctx_t *ctx)
 }
 
 /**
  * Create an event loop context
  * @param ctx       Pointer to an uev_ctx_t context to be initialized
- * @param maxevents Maximum number of events in event cache
+ * @param maxevents Maximum number of events in event cache [1, 10]
  *
  * This function is the same as uev_init() except for the @p maxevents
- * argument, which controls the number of events in the event cache
- * returned to the main loop.
+ * argument, max ::UEV_MAX_EVENTS, which controls the number of events
+ * in the event cache returned to the main loop.
  *
  * In cases where you have multiple events pending in the cache and some
  * event may cause later ones, already sent by the kernel to userspace,
  * to be deleted the pointer returned to the event loop for this later
  * event may be deleted.
@@ -220,10 +220,13 @@ int uev_init1(uev_ctx_t *ctx, int maxevents)
 	if (!ctx || maxevents < 1) {
 		errno = EINVAL;
 		return -1;
 	}
 
+	if (maxevents > UEV_MAX_EVENTS)
+		maxevents = UEV_MAX_EVENTS;
+
 	memset(ctx, 0, sizeof(*ctx));
 	ctx->maxevents = maxevents;
 
 	return _init(ctx, 0);
 }
@@ -317,12 +320,16 @@ int uev_run(uev_ctx_t *ctx, int flags)
 			uev_timer_set(w, w->u.t.timeout, w->u.t.period);
 	}
 
 	while (ctx->running && ctx->watchers) {
 		struct epoll_event ee[UEV_MAX_EVENTS];
+		int maxevents = ctx->maxevents;
 		int i, nfds, rerun = 0;
 
+		if (maxevents > UEV_MAX_EVENTS)
+			maxevents = UEV_MAX_EVENTS;
+
 		/* Handle special case: `application < file.txt` */
 		if (ctx->workaround) {
 			_UEV_FOREACH(w, ctx->watchers) {
 				if (w->active != -1 || !w->cb)
 					continue;
@@ -339,11 +346,11 @@ int uev_run(uev_ctx_t *ctx, int flags)
 
 		if (rerun)
 			continue;
 		ctx->workaround = 0;
 
-		while ((nfds = epoll_wait(ctx->fd, ee, ctx->maxevents, timeout)) < 0) {
+		while ((nfds = epoll_wait(ctx->fd, ee, maxevents, timeout)) < 0) {
 			if (!ctx->running)
 				break;
 
 			if (EINTR == errno)
 				continue; /* Signalled, try again */

Vulnerable code:  * @param maxevents Maximum number of events in event cache
 * argument, which controls the number of events in the event cache
 * returned to the main loop.
		while ((nfds = epoll_wait(ctx->fd, ee, ctx->maxevents, timeout)) < 0) {
Fixed code:  * @param maxevents Maximum number of events in event cache [1, 10]
 * argument, max ::UEV_MAX_EVENTS, which controls the number of events
 * in the event cache returned to the main loop.
	if (maxevents > UEV_MAX_EVENTS)
		maxevents = UEV_MAX_EVENTS;

		int maxevents = ctx->maxevents;
		if (maxevents > UEV_MAX_EVENTS)
			maxevents = UEV_MAX_EVENTS;

		while ((nfds = epoll_wait(ctx->fd, ee, maxevents, timeout)) < 0) {