Browse Source

[core] use readdir instead of readdir_r

readdir_r is deprecated in glibc due to serious memory handling issues
in the API: one cannot pass the size of the allocated dirent.

glibc authors claims readdir is thread-safe in modern implementations,
and expect POSIX to require it in a future version.

No way to check whether readdir is thread-safe though :(

("thread-safe" in this context means different directory streams, which
is good enough.)

Also remove li_dirent_buf_size.

Change-Id: Ia5eae3327e97dc4b0751fb2604ea21c0ce09a5f9
master
Stefan Bühler 3 years ago
parent
commit
bc6b256c34
3 changed files with 10 additions and 43 deletions
  1. +0
    -2
      include/lighttpd/utils.h
  2. +0
    -30
      src/common/utils.c
  3. +10
    -11
      src/main/stat_cache.c

+ 0
- 2
include/lighttpd/utils.h View File

@@ -81,8 +81,6 @@ LI_API gboolean li_string_suffix(const GString *str, const gchar *s, gsize len);

LI_API void li_string_append_int(GString *dest, gint64 val);

LI_API gsize li_dirent_buf_size(DIR * dirp);

LI_API void li_apr_sha1_base64(GString *dest, const GString *passwd);
LI_API void li_apr_md5_crypt(GString *dest, const GString *password, const GString *salt);



+ 0
- 30
src/common/utils.c View File

@@ -851,36 +851,6 @@ void li_string_append_int(GString *dest, gint64 v) {
dest->len = len;
}

/* http://womble.decadentplace.org.uk/readdir_r-advisory.html */
gsize li_dirent_buf_size(DIR * dirp) {
glong name_max;
gsize name_end;

# if !defined(HAVE_DIRFD)
UNUSED(dirp);
# endif

# if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
if (name_max == -1)
# if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
# else
return (gsize)(-1);
# endif
# else
# if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
# else
# error "buffer size for readdir_r cannot be determined"
# endif
# endif

name_end = (gsize)offsetof(struct dirent, d_name) + name_max + 1;

return (name_end > sizeof(struct dirent) ? name_end : sizeof(struct dirent));
}

const char *li_remove_path(const char *path) {
char *p = strrchr(path, G_DIR_SEPARATOR);
if (NULL != p && *(p) != '\0') {


+ 10
- 11
src/main/stat_cache.c View File

@@ -108,10 +108,7 @@ static void stat_cache_run(gpointer data) {
if (!sce->data.failed && sce->type == STAT_CACHE_ENTRY_DIR) {
/* dirlisting */
DIR *dirp;
gsize size;
struct dirent *entry;
struct dirent *result;
gint error;
liStatCacheEntryData sced;
GString *str;

@@ -120,16 +117,19 @@ static void stat_cache_run(gpointer data) {
sce->data.failed = TRUE;
sce->data.err = errno;
} else {
size = li_dirent_buf_size(dirp);
LI_FORCE_ASSERT(size != (gsize)-1);
entry = g_slice_alloc(size);

sce->dirlist = g_array_sized_new(FALSE, FALSE, sizeof(liStatCacheEntryData), 32);

str = g_string_sized_new(sce->data.path->len + 64);
g_string_append_len(str, GSTR_LEN(sce->data.path));

while ((error = readdir_r(dirp, entry, &result)) == 0 && result != NULL) {
/* glibc claims modern readdir are thread-safe, and
* readdir_r has issues. no way to check readdir is actually
* safe, hope for the best.
*/
for (;;) {
errno = 0; /* readdir may not reset errno */
if (NULL == (result = readdir(dirp))) break;

/* hide "." and ".." */
if (result->d_name[0] == '.' && (result->d_name[1] == '\0' ||
(result->d_name[1] == '.' && result->d_name[2] == '\0'))) {
@@ -155,13 +155,12 @@ static void stat_cache_run(gpointer data) {
g_array_append_val(sce->dirlist, sced);
}

if (error) {
if (errno) {
sce->data.failed = TRUE;
sce->data.err = error;
sce->data.err = errno;
}

g_string_free(str, TRUE);
g_slice_free1(size, entry);
closedir(dirp);
}
}


Loading…
Cancel
Save