elf: Load files from /etc/ld.so.preload.d
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
success
|
Test passed
|
Commit Message
From: Łukasz Stelmach <l.stelmach@samsung.com>
Enable reading a list of libraries to preload from files in
/etc/ld.so.preload.d directory. The files must end in `.conf` to be read.
Fixes: https://sourceware.org/bugzilla/show_bug.cgi?id=24913
Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
---
elf/rtld.c | 161 ++++++++++++++++++++++++++++++++---------------------
1 file changed, 97 insertions(+), 64 deletions(-)
Comments
On 9/29/26 1:50 PM, Łukasz Stelmach wrote:
> From: Łukasz Stelmach <l.stelmach@samsung.com>
>
> Enable reading a list of libraries to preload from files in
> /etc/ld.so.preload.d directory. The files must end in `.conf` to be read.
>
> Fixes: https://sourceware.org/bugzilla/show_bug.cgi?id=24913
> Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
> ---
> elf/rtld.c | 161 ++++++++++++++++++++++++++++++++---------------------
> 1 file changed, 97 insertions(+), 64 deletions(-)
>
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 324393d606..74387c2663 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -54,6 +54,8 @@
> #include <dl-audit-check.h>
> #include <dl-call_tls_init_tp.h>
> #include <dl-exec-post.h>
> +#include <sys/types.h>
> +#include <dirent.h>
>
> #include <assert.h>
>
> @@ -1841,80 +1843,111 @@ dl_main (const ElfW(Phdr) *phdr,
> the work but this does not matter, since it is not for production
> use. */
> static const char preload_file[] = "/etc/ld.so.preload";
> + const size_t preload_dir_len = sizeof("/etc/ld.so.preload.d") - 1;
> + char preload_dir[sizeof("/etc/ld.so.preload.d") + 1 + sizeof(((struct dirent*)0)->d_name)] = "/etc/ld.so.preload.d";
> + const char *p_file = NULL;
> + DIR *p_dir = NULL;
> +
> if (__glibc_unlikely (__access (preload_file, R_OK) == 0))
> + p_file = preload_file;
> +
> + if (__glibc_unlikely (__access (preload_dir, R_OK | X_OK) == 0))
> + p_dir = opendir(preload_dir);
> +
> + if (__glibc_unlikely (p_file != NULL || p_dir != NULL))
> {
> - /* Read the contents of the file. */
> - file = _dl_sysdep_read_whole_file (preload_file, &file_size,
> - PROT_READ | PROT_WRITE);
> - if (__glibc_unlikely (file != MAP_FAILED))
> - {
> - /* Parse the file. It contains names of libraries to be loaded,
> - separated by white spaces or `:'. It may also contain
> - comments introduced by `#'. */
> - char *problem;
> - char *runp;
> - size_t rest;
> -
> - /* Eliminate comments. */
> - runp = file;
> - rest = file_size;
> - while (rest > 0)
> - {
> - char *comment = memchr (runp, '#', rest);
> - if (comment == NULL)
> - break;
> + struct dirent* de = NULL;
> + do {
> + if (de != NULL)
> + {
> + int l = strlen(de->d_name);
> + if (__glibc_unlikely ((l < sizeof(".conf") - 1)
> + || (strncmp(".conf", &de->d_name[l - 5], 5) != 0)))
> + continue;
> + preload_dir[preload_dir_len] = '/';
> + strncpy(&preload_dir[preload_dir_len + 1], de->d_name, sizeof(preload_dir) - preload_dir_len - 1);
> + preload_dir[preload_dir_len + 1 + l] = '\0';
> + p_file = preload_dir;
> + }
>
> - rest -= comment - runp;
> - do
> - *comment = ' ';
> - while (--rest > 0 && *++comment != '\n');
> - }
> + if (p_file == NULL)
> + continue;
>
> - /* We have one problematic case: if we have a name at the end of
> - the file without a trailing terminating characters, we cannot
> - place the \0. Handle the case separately. */
> - if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t'
> - && file[file_size - 1] != '\n' && file[file_size - 1] != ':')
> - {
> - problem = &file[file_size];
> - while (problem > file && problem[-1] != ' '
> - && problem[-1] != '\t'
> - && problem[-1] != '\n' && problem[-1] != ':')
> - --problem;
> -
> - if (problem > file)
> - problem[-1] = '\0';
> - }
> - else
> - {
> - problem = NULL;
> - file[file_size - 1] = '\0';
> - }
> + /* Read the contents of the file. */
> + file = _dl_sysdep_read_whole_file (p_file, &file_size,
> + PROT_READ | PROT_WRITE);
> + if (__glibc_unlikely (file != MAP_FAILED))
> + {
> + /* Parse the file. It contains names of libraries to be loaded,
> + separated by white spaces or `:'. It may also contain
> + comments introduced by `#'. */
> + char *problem;
> + char *runp;
> + size_t rest;
> +
> + /* Eliminate comments. */
> + runp = file;
> + rest = file_size;
> + while (rest > 0)
> + {
> + char *comment = memchr (runp, '#', rest);
> + if (comment == NULL)
> + break;
> +
> + rest -= comment - runp;
> + do
> + *comment = ' ';
> + while (--rest > 0 && *++comment != '\n');
> + }
>
> - RTLD_TIMING_VAR (start);
> - rtld_timer_start (&start);
> + /* We have one problematic case: if we have a name at the end of
> + the file without a trailing terminating characters, we cannot
> + place the \0. Handle the case separately. */
> + if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t'
> + && file[file_size - 1] != '\n' && file[file_size - 1] != ':')
> + {
> + problem = &file[file_size];
> + while (problem > file && problem[-1] != ' '
> + && problem[-1] != '\t'
> + && problem[-1] != '\n' && problem[-1] != ':')
> + --problem;
> +
> + if (problem > file)
> + problem[-1] = '\0';
> + }
> + else
> + {
> + problem = NULL;
> + file[file_size - 1] = '\0';
> + }
>
> - if (file != problem)
> - {
> - char *p;
> - runp = file;
> - while ((p = strsep (&runp, ": \t\n")) != NULL)
> - if (p[0] != '\0')
> - npreloads += do_preload (p, main_map, preload_file);
> - }
> + RTLD_TIMING_VAR (start);
> + rtld_timer_start (&start);
>
> - if (problem != NULL)
> - {
> - char *p = strndupa (problem, file_size - (problem - file));
> + if (file != problem)
> + {
> + char *p;
> + runp = file;
> + while ((p = strsep (&runp, ": \t\n")) != NULL)
> + if (p[0] != '\0')
> + npreloads += do_preload (p, main_map, p_file);
> + }
>
> - npreloads += do_preload (p, main_map, preload_file);
> - }
> + if (problem != NULL)
> + {
> + char *p = strndupa (problem, file_size - (problem - file));
>
> - rtld_timer_accum (&load_time, start);
> + npreloads += do_preload (p, main_map, p_file);
> + }
>
> - /* We don't need the file anymore. */
> - __munmap (file, file_size);
> - }
> + rtld_timer_accum (&load_time, start);
> +
> + /* We don't need the file anymore. */
> + __munmap (file, file_size);
> + }
> + } while (p_dir && (de = readdir(p_dir)));
> + if (p_dir != NULL)
> + closedir(p_dir);
> }
>
> if (__glibc_unlikely (*first_preload != NULL))
...How does the ordering work? If it's just readdir, isn't that
effectively unspecified? AIUI all the other .d directory conventions in
most tools sort entries, which would matter here for e.g. symbol
interposition order, I'd think...
It was 2026-09-30 śro 00:53, when Gabriel Ravier wrote:
> On 9/29/26 1:50 PM, Łukasz Stelmach wrote:
>> From: Łukasz Stelmach <l.stelmach@samsung.com>
>>
>> Enable reading a list of libraries to preload from files in
>> /etc/ld.so.preload.d directory. The files must end in `.conf` to be read.
>>
>> Fixes: https://sourceware.org/bugzilla/show_bug.cgi?id=24913
>> Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
>> ---
>> elf/rtld.c | 161 ++++++++++++++++++++++++++++++++---------------------
>> 1 file changed, 97 insertions(+), 64 deletions(-)
>>
>> diff --git a/elf/rtld.c b/elf/rtld.c
>> index 324393d606..74387c2663 100644
>> --- a/elf/rtld.c
>> +++ b/elf/rtld.c
>> @@ -54,6 +54,8 @@
>> #include <dl-audit-check.h>
>> #include <dl-call_tls_init_tp.h>
>> #include <dl-exec-post.h>
>> +#include <sys/types.h>
>> +#include <dirent.h>
>> #include <assert.h>
>> @@ -1841,80 +1843,111 @@ dl_main (const ElfW(Phdr) *phdr,
>> the work but this does not matter, since it is not for production
>> use. */
>> static const char preload_file[] = "/etc/ld.so.preload";
>> + const size_t preload_dir_len = sizeof("/etc/ld.so.preload.d") - 1;
>> + char preload_dir[sizeof("/etc/ld.so.preload.d") + 1 + sizeof(((struct dirent*)0)->d_name)] = "/etc/ld.so.preload.d";
>> + const char *p_file = NULL;
>> + DIR *p_dir = NULL;
>> +
>> if (__glibc_unlikely (__access (preload_file, R_OK) == 0))
>> + p_file = preload_file;
>> +
>> + if (__glibc_unlikely (__access (preload_dir, R_OK | X_OK) == 0))
>> + p_dir = opendir(preload_dir);
>> +
>> + if (__glibc_unlikely (p_file != NULL || p_dir != NULL))
>> {
>> - /* Read the contents of the file. */
>> - file = _dl_sysdep_read_whole_file (preload_file, &file_size,
>> - PROT_READ | PROT_WRITE);
>> - if (__glibc_unlikely (file != MAP_FAILED))
>> - {
>> - /* Parse the file. It contains names of libraries to be loaded,
>> - separated by white spaces or `:'. It may also contain
>> - comments introduced by `#'. */
>> - char *problem;
>> - char *runp;
>> - size_t rest;
>> -
>> - /* Eliminate comments. */
>> - runp = file;
>> - rest = file_size;
>> - while (rest > 0)
>> - {
>> - char *comment = memchr (runp, '#', rest);
>> - if (comment == NULL)
>> - break;
>> + struct dirent* de = NULL;
>> + do {
>> + if (de != NULL)
[...]
>> + /* We don't need the file anymore. */
>> + __munmap (file, file_size);
>> + }
>> + } while (p_dir && (de = readdir(p_dir)));
>> + if (p_dir != NULL)
>> + closedir(p_dir);
>> }
>> if (__glibc_unlikely (*first_preload != NULL))
>
>
> ...How does the ordering work? If it's just readdir, isn't that
> effectively unspecified? AIUI all the other .d directory conventions
> in most tools sort entries, which would matter here for e.g. symbol
> interposition order, I'd think...
Indeed there is no ordering, yet. TODO. Thanks.
@@ -54,6 +54,8 @@
#include <dl-audit-check.h>
#include <dl-call_tls_init_tp.h>
#include <dl-exec-post.h>
+#include <sys/types.h>
+#include <dirent.h>
#include <assert.h>
@@ -1841,80 +1843,111 @@ dl_main (const ElfW(Phdr) *phdr,
the work but this does not matter, since it is not for production
use. */
static const char preload_file[] = "/etc/ld.so.preload";
+ const size_t preload_dir_len = sizeof("/etc/ld.so.preload.d") - 1;
+ char preload_dir[sizeof("/etc/ld.so.preload.d") + 1 + sizeof(((struct dirent*)0)->d_name)] = "/etc/ld.so.preload.d";
+ const char *p_file = NULL;
+ DIR *p_dir = NULL;
+
if (__glibc_unlikely (__access (preload_file, R_OK) == 0))
+ p_file = preload_file;
+
+ if (__glibc_unlikely (__access (preload_dir, R_OK | X_OK) == 0))
+ p_dir = opendir(preload_dir);
+
+ if (__glibc_unlikely (p_file != NULL || p_dir != NULL))
{
- /* Read the contents of the file. */
- file = _dl_sysdep_read_whole_file (preload_file, &file_size,
- PROT_READ | PROT_WRITE);
- if (__glibc_unlikely (file != MAP_FAILED))
- {
- /* Parse the file. It contains names of libraries to be loaded,
- separated by white spaces or `:'. It may also contain
- comments introduced by `#'. */
- char *problem;
- char *runp;
- size_t rest;
-
- /* Eliminate comments. */
- runp = file;
- rest = file_size;
- while (rest > 0)
- {
- char *comment = memchr (runp, '#', rest);
- if (comment == NULL)
- break;
+ struct dirent* de = NULL;
+ do {
+ if (de != NULL)
+ {
+ int l = strlen(de->d_name);
+ if (__glibc_unlikely ((l < sizeof(".conf") - 1)
+ || (strncmp(".conf", &de->d_name[l - 5], 5) != 0)))
+ continue;
+ preload_dir[preload_dir_len] = '/';
+ strncpy(&preload_dir[preload_dir_len + 1], de->d_name, sizeof(preload_dir) - preload_dir_len - 1);
+ preload_dir[preload_dir_len + 1 + l] = '\0';
+ p_file = preload_dir;
+ }
- rest -= comment - runp;
- do
- *comment = ' ';
- while (--rest > 0 && *++comment != '\n');
- }
+ if (p_file == NULL)
+ continue;
- /* We have one problematic case: if we have a name at the end of
- the file without a trailing terminating characters, we cannot
- place the \0. Handle the case separately. */
- if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t'
- && file[file_size - 1] != '\n' && file[file_size - 1] != ':')
- {
- problem = &file[file_size];
- while (problem > file && problem[-1] != ' '
- && problem[-1] != '\t'
- && problem[-1] != '\n' && problem[-1] != ':')
- --problem;
-
- if (problem > file)
- problem[-1] = '\0';
- }
- else
- {
- problem = NULL;
- file[file_size - 1] = '\0';
- }
+ /* Read the contents of the file. */
+ file = _dl_sysdep_read_whole_file (p_file, &file_size,
+ PROT_READ | PROT_WRITE);
+ if (__glibc_unlikely (file != MAP_FAILED))
+ {
+ /* Parse the file. It contains names of libraries to be loaded,
+ separated by white spaces or `:'. It may also contain
+ comments introduced by `#'. */
+ char *problem;
+ char *runp;
+ size_t rest;
+
+ /* Eliminate comments. */
+ runp = file;
+ rest = file_size;
+ while (rest > 0)
+ {
+ char *comment = memchr (runp, '#', rest);
+ if (comment == NULL)
+ break;
+
+ rest -= comment - runp;
+ do
+ *comment = ' ';
+ while (--rest > 0 && *++comment != '\n');
+ }
- RTLD_TIMING_VAR (start);
- rtld_timer_start (&start);
+ /* We have one problematic case: if we have a name at the end of
+ the file without a trailing terminating characters, we cannot
+ place the \0. Handle the case separately. */
+ if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t'
+ && file[file_size - 1] != '\n' && file[file_size - 1] != ':')
+ {
+ problem = &file[file_size];
+ while (problem > file && problem[-1] != ' '
+ && problem[-1] != '\t'
+ && problem[-1] != '\n' && problem[-1] != ':')
+ --problem;
+
+ if (problem > file)
+ problem[-1] = '\0';
+ }
+ else
+ {
+ problem = NULL;
+ file[file_size - 1] = '\0';
+ }
- if (file != problem)
- {
- char *p;
- runp = file;
- while ((p = strsep (&runp, ": \t\n")) != NULL)
- if (p[0] != '\0')
- npreloads += do_preload (p, main_map, preload_file);
- }
+ RTLD_TIMING_VAR (start);
+ rtld_timer_start (&start);
- if (problem != NULL)
- {
- char *p = strndupa (problem, file_size - (problem - file));
+ if (file != problem)
+ {
+ char *p;
+ runp = file;
+ while ((p = strsep (&runp, ": \t\n")) != NULL)
+ if (p[0] != '\0')
+ npreloads += do_preload (p, main_map, p_file);
+ }
- npreloads += do_preload (p, main_map, preload_file);
- }
+ if (problem != NULL)
+ {
+ char *p = strndupa (problem, file_size - (problem - file));
- rtld_timer_accum (&load_time, start);
+ npreloads += do_preload (p, main_map, p_file);
+ }
- /* We don't need the file anymore. */
- __munmap (file, file_size);
- }
+ rtld_timer_accum (&load_time, start);
+
+ /* We don't need the file anymore. */
+ __munmap (file, file_size);
+ }
+ } while (p_dir && (de = readdir(p_dir)));
+ if (p_dir != NULL)
+ closedir(p_dir);
}
if (__glibc_unlikely (*first_preload != NULL))