Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250710185131.186-1-mailto.luca.kellermann@gmail.com>
Date: Thu, 10 Jul 2025 20:51:28 +0200
From: Luca Kellermann <mailto.luca.kellermann@...il.com>
To: musl@...ts.openwall.com
Subject: [PATCH 1/4] scandir: hide that errno is set to 0

POSIX.1-2024 requires that standard functions don't set errno to 0.
commit dae17a1aaf25d8333e729173d86659066607d87d ensured that cmp() and
the caller of scandir() cannot observe that errno is set to 0
internally. however, this was not yet the case for the sel() callback.

the following program demonstrates the issue:

#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
static int sel(const struct dirent *e)
{
	/* XSH errno / XSH 2.3 Error Numbers: "No function in this
	 * volume of POSIX.1-2024 shall set errno to [0 / zero]." */
	assert(errno != 0);
	return 1;
}
int main(void)
{
	/* XSH errno: "The value of errno in the initial thread shall
	 * be zero at program startup [...]" */
	assert(errno == 0);
	errno = 1;
	struct dirent **es = 0;
	int n = scandir(".", &es, sel, alphasort);
	if (n == -1) {
		perror("scandir");
		exit(EXIT_FAILURE);
	}
	while (n > 0) free(es[--n]);
	free(es);
}

$ ./scandir-errno
Assertion failed: errno != 0 (scandir-errno.c: sel: 10)
Aborted (core dumped)
---
 src/dirent/scandir.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/dirent/scandir.c b/src/dirent/scandir.c
index 7456b9b8..eead7e50 100644
--- a/src/dirent/scandir.c
+++ b/src/dirent/scandir.c
@@ -17,7 +17,11 @@ int scandir(const char *path, struct dirent ***res,
 	if (!d) return -1;
 
 	while ((errno=0), (de = readdir(d))) {
-		if (sel && !sel(de)) continue;
+		if (sel) {
+			/* sel() must not observe that errno was set to 0. */
+			errno = old_errno;
+			if (!sel(de)) continue;
+		}
 		if (cnt >= len) {
 			len = 2*len+1;
 			if (len > SIZE_MAX/sizeof *names) break;
@@ -37,6 +41,7 @@ int scandir(const char *path, struct dirent ***res,
 		free(names);
 		return -1;
 	}
+	/* cmp() and caller must not observe that errno was set to 0. */
 	errno = old_errno;
 
 	if (cmp) qsort(names, cnt, sizeof *names, (int (*)(const void *, const void *))cmp);
-- 
2.43.0

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.