rtld: preserve the 'seen' state of the dlerror message in errmsg_save()

rtld preserves its current error message around calls to user init/fini
lists, to not override original error with potential secondary errors
caused by user code recursing into rtld.  After 4d9128da54,
the preservation of the string itself is not enough, the 'seen'
indicator must be preserved as well.  Otherwise, since new code does not
clear string (it cannot), call to _rtld_error() from errmsg_restore()
revived whatever message was consumed last.

Change errmsg_save() to return structure recording both 'seen' indicator
and the message, if any.

PR:	255698
Reported by:	Eugene M. Kim <astralblue@gmail.com>
Sponsored by:	The FreeBSD Foundation
MFC after:	3 days
This commit is contained in:
Konstantin Belousov 2021-05-10 22:02:19 +03:00
parent 5e7cdf1817
commit 630caa95d4

View File

@ -81,6 +81,10 @@ extern struct r_debug r_debug; /* For GDB */
extern int _thread_autoinit_dummy_decl;
extern void (*__cleanup)(void);
struct dlerror_save {
int seen;
char *msg;
};
/*
* Function declarations.
@ -100,8 +104,8 @@ static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj,
static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int);
static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
static bool donelist_check(DoneList *, const Obj_Entry *);
static void errmsg_restore(char *);
static char *errmsg_save(void);
static void errmsg_restore(struct dlerror_save *);
static struct dlerror_save *errmsg_save(void);
static void *fill_search_info(const char *, size_t, void *);
static char *find_library(const char *, const Obj_Entry *, int *);
static const char *gethints(bool);
@ -941,10 +945,16 @@ _rtld_error(const char *fmt, ...)
/*
* Return a dynamically-allocated copy of the current error message, if any.
*/
static char *
static struct dlerror_save *
errmsg_save(void)
{
return (xstrdup(lockinfo.dlerror_loc()));
struct dlerror_save *res;
res = xmalloc(sizeof(*res));
res->seen = *lockinfo.dlerror_seen();
if (res->seen == 0)
res->msg = xstrdup(lockinfo.dlerror_loc());
return (res);
}
/*
@ -952,14 +962,17 @@ errmsg_save(void)
* by errmsg_save(). The copy is freed.
*/
static void
errmsg_restore(char *saved_msg)
errmsg_restore(struct dlerror_save *saved_msg)
{
if (saved_msg == NULL)
_rtld_error("");
else {
_rtld_error("%s", saved_msg);
free(saved_msg);
if (saved_msg == NULL || saved_msg->seen == 1) {
*lockinfo.dlerror_seen() = 1;
} else {
*lockinfo.dlerror_seen() = 0;
strlcpy(lockinfo.dlerror_loc(), saved_msg->msg,
lockinfo.dlerror_loc_sz);
free(saved_msg->msg);
}
free(saved_msg);
}
static const char *
@ -2736,7 +2749,7 @@ static void
objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate)
{
Objlist_Entry *elm;
char *saved_msg;
struct dlerror_save *saved_msg;
Elf_Addr *fini_addr;
int index;
@ -2812,7 +2825,7 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate)
{
Objlist_Entry *elm;
Obj_Entry *obj;
char *saved_msg;
struct dlerror_save *saved_msg;
Elf_Addr *init_addr;
void (*reg)(void (*)(void));
int index;