From: Tudor-Stefan Magirescu <tudor.magirescu@gmail.com>
To: gdb-patches@sourceware.org
Cc: tudor.magirescu@gmail.com
Subject: [PATCH v2 2/2] [gdb] Retry blocked auto-load scripts when safe-path is widened
Date: Thu, 3 Sep 2026 11:22:01 +0200 [thread overview]
Message-ID: <20260903092201.663063-3-tudor.magirescu@gmail.com> (raw)
In-Reply-To: <20260903092201.663063-1-tudor.magirescu@gmail.com>
Extend add-auto-load-safe-path to immediately retry scripts that were
previously blocked by the safe-path. Enhance the warning printed when
auto-loading is declined to inform the user that add-auto-load-safe-path
can be used to load the script in the current session.
A new boolean parameter, from_objfile_load, is propagated through the
auto-load call chain to distinguish a new objfile load from a safe-path
refresh. Scripts already loaded are skipped during a refresh to avoid
re-execution. Safe-path warnings are suppressed during the refresh pass
since no new information would be conveyed.
Extend gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event
to verify that after a script is blocked on the first run, calling
add-auto-load-safe-path immediately loads it without a re-run of the
inferior.
Tested on x86_64-linux-gnu and found no regressions.
---
gdb/auto-load.c | 105 ++++++++++++------
gdb/auto-load.h | 13 ++-
gdb/extension.c | 7 +-
gdb/extension.h | 3 +-
...ed-pretty-printers-in-newobjfile-event.exp | 31 +++++-
5 files changed, 112 insertions(+), 47 deletions(-)
diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 8a6e818fd69..35d6ae3d8f2 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -358,6 +358,10 @@ Use 'set auto-load safe-path /' for disabling the auto-load safe-path security.\
DIRNAME_SEPARATOR, args);
auto_load_safe_path_vec_update ();
+
+ if (current_program_space != NULL)
+ for (struct objfile &objfile : current_program_space->objfiles ())
+ load_auto_scripts_for_objfile (objfile, false);
}
/* "add-auto-load-scripts-directory" command for the auto_load_dir configuration
@@ -495,7 +499,7 @@ filename_is_in_auto_load_safe_path_vec (const char *filename,
/* See auto-load.h. */
bool
-file_is_auto_load_safe (const char *filename)
+file_is_auto_load_safe (const char *filename, bool print_warning)
{
gdb::unique_xmalloc_ptr<char> filename_real;
static bool advice_printed = false;
@@ -507,6 +511,9 @@ file_is_auto_load_safe (const char *filename)
if (filename_is_in_auto_load_safe_path_vec (filename, &filename_real))
return true;
+ if (!print_warning)
+ return false;
+
warning (_("File \"%ps\" auto-loading has been declined by your "
"`auto-load safe-path' set to \"%s\"."),
styled_string (file_name_style.style (), filename_real.get ()),
@@ -538,7 +545,8 @@ file_is_auto_load_safe (const char *filename)
gdb_printf (_("\
To enable execution of this file add\n\
\t%p[add-auto-load-safe-path %s%p]\n\
-line to your configuration file \"%ps\".\n\
+line to your configuration file \"%ps\", or run that command to load it\n\
+in the current session.\n\
To completely disable this security protection add\n\
\t%ps\n\
line to your configuration file \"%ps\".\n\
@@ -778,7 +786,8 @@ clear_section_scripts (program_space *pspace)
static int
auto_load_objfile_script_1 (struct objfile *objfile, const char *realname,
- const struct extension_language_defn *language)
+ const struct extension_language_defn *language,
+ bool from_objfile_load)
{
const char *debugfile;
int retval;
@@ -839,16 +848,18 @@ auto_load_objfile_script_1 (struct objfile *objfile, const char *realname,
("Loading %s script \"%s\" by extension for objfile \"%s\".",
ext_lang_name (language), debugfile, objfile_name (objfile));
- bool is_safe = file_is_auto_load_safe (debugfile);
+ bool is_safe = file_is_auto_load_safe (debugfile, from_objfile_load);
/* Add this script to the hash table too so
"info auto-load ${lang}-scripts" can print it. */
pspace_info
= get_auto_load_pspace_data_for_loading (objfile->pspace ());
- maybe_add_script_file (pspace_info, is_safe, debugfile, debugfile,
- language);
- /* Execute the script if it is on the safe path. */
- if (is_safe)
+ bool was_loaded = maybe_add_script_file (pspace_info, is_safe, debugfile,
+ debugfile, language);
+ /* Execute the script if it is on the safe path. When called from
+ add-auto-load-safe-path, skip scripts that were already loaded to
+ avoid re-executing them. */
+ if (is_safe && (from_objfile_load || !was_loaded))
{
objfile_script_sourcer_func *sourcer
= ext_lang_objfile_script_sourcer (language);
@@ -873,7 +884,8 @@ auto_load_objfile_script_1 (struct objfile *objfile, const char *realname,
void
auto_load_objfile_script (struct objfile *objfile,
- const struct extension_language_defn *language)
+ const struct extension_language_defn *language,
+ bool from_objfile_load)
{
AUTO_LOAD_SCOPED_DEBUG_ENTER_EXIT;
auto_load_debug_printf ("objfile: %s, language: %s",
@@ -883,7 +895,8 @@ auto_load_objfile_script (struct objfile *objfile,
gdb::unique_xmalloc_ptr<char> realname
= gdb_realpath (objfile_name (objfile));
- if (auto_load_objfile_script_1 (objfile, realname.get (), language))
+ if (auto_load_objfile_script_1 (objfile, realname.get (), language,
+ from_objfile_load))
return;
/* For Windows/DOS .exe executables, strip the .exe suffix, so that
@@ -900,7 +913,8 @@ auto_load_objfile_script (struct objfile *objfile,
auto_load_debug_printf
("Stripped .exe suffix, retrying with \"%s\".", realname.get ());
- auto_load_objfile_script_1 (objfile, realname.get (), language);
+ auto_load_objfile_script_1 (objfile, realname.get (), language,
+ from_objfile_load);
return;
}
@@ -934,8 +948,8 @@ auto_load_objfile_script (struct objfile *objfile,
("Debug filename mismatch, retrying with \"%s\".",
p_realname.c_str ());
- auto_load_objfile_script_1 (objfile,
- p_realname.c_str (), language);
+ auto_load_objfile_script_1 (objfile, p_realname.c_str (),
+ language, from_objfile_load);
}
}
}
@@ -950,7 +964,7 @@ source_script_file (struct auto_load_pspace_info *pspace_info,
struct objfile *objfile,
const struct extension_language_defn *language,
const char *section_name, unsigned int offset,
- const char *file)
+ const char *file, bool from_objfile_load)
{
objfile_script_sourcer_func *sourcer;
@@ -983,7 +997,7 @@ source_script_file (struct auto_load_pspace_info *pspace_info,
ext_lang_name (language), opened->full_path.get (),
section_name, objfile_name (objfile));
- if (!file_is_auto_load_safe (opened->full_path.get ()))
+ if (!file_is_auto_load_safe (opened->full_path.get (), from_objfile_load))
opened.reset ();
}
else
@@ -1000,12 +1014,14 @@ source_script_file (struct auto_load_pspace_info *pspace_info,
section_name, offset);
}
- maybe_add_script_file (pspace_info, bool (opened), file,
- (opened ? opened->full_path.get (): NULL),
- language);
+ bool was_loaded = maybe_add_script_file (pspace_info, bool (opened), file,
+ (opened ? opened->full_path.get ()
+ : NULL), language);
- /* Execute the script if it is on the safe path. */
- if (opened)
+ /* Execute the script if it is on the safe path. When called from
+ add-auto-load-safe-path, skip scripts that were already loaded to
+ avoid re-executing them. */
+ if (opened && (from_objfile_load || !was_loaded))
sourcer (language, objfile, opened->stream.get (),
opened->full_path.get ());
}
@@ -1019,7 +1035,7 @@ execute_script_contents (struct auto_load_pspace_info *pspace_info,
struct objfile *objfile,
const struct extension_language_defn *language,
const char *section_name, unsigned int offset,
- const char *script)
+ const char *script, bool from_objfile_load)
{
objfile_script_executor_func *executor;
const char *newline, *script_text;
@@ -1081,12 +1097,16 @@ of file %ps."),
("Loading %s script \"%s\" from section \"%s\" of objfile \"%s\".",
ext_lang_name (language), name, section_name, objfile_name (objfile));
- bool is_safe = file_is_auto_load_safe (objfile_name (objfile));
+ bool is_safe = file_is_auto_load_safe (objfile_name (objfile),
+ from_objfile_load);
- maybe_add_script_text (pspace_info, is_safe, name, language);
+ bool was_loaded = maybe_add_script_text (pspace_info, is_safe, name,
+ language);
- /* Execute the script if it is on the safe path. */
- if (is_safe)
+ /* Execute the script if it is on the safe path. When called from
+ add-auto-load-safe-path, skip scripts that were already loaded to
+ avoid re-executing them. */
+ if (is_safe && (from_objfile_load || !was_loaded))
executor (language, objfile, name, script_text);
}
@@ -1104,7 +1124,8 @@ of file %ps."),
static void
source_section_scripts (struct objfile *objfile, const char *section_name,
- const char *start, const char *end)
+ const char *start, const char *end,
+ bool from_objfile_load)
{
auto_load_pspace_info *pspace_info
= get_auto_load_pspace_data_for_loading (objfile->pspace ());
@@ -1155,12 +1176,13 @@ source_section_scripts (struct objfile *objfile, const char *section_name,
continue;
}
source_script_file (pspace_info, objfile, language,
- section_name, offset, entry);
+ section_name, offset, entry, from_objfile_load);
break;
case SECTION_SCRIPT_ID_PYTHON_TEXT:
case SECTION_SCRIPT_ID_SCHEME_TEXT:
execute_script_contents (pspace_info, objfile, language,
- section_name, offset, entry);
+ section_name, offset, entry,
+ from_objfile_load);
break;
}
}
@@ -1169,7 +1191,8 @@ source_section_scripts (struct objfile *objfile, const char *section_name,
/* Load scripts specified in section SECTION_NAME of OBJFILE. */
static void
-auto_load_section_scripts (struct objfile *objfile, const char *section_name)
+auto_load_section_scripts (struct objfile *objfile, const char *section_name,
+ bool from_objfile_load)
{
bfd *abfd = objfile->obfd.get ();
asection *scripts_sect;
@@ -1188,7 +1211,8 @@ auto_load_section_scripts (struct objfile *objfile, const char *section_name)
else
{
const char *p = (const char *) data.data ();
- source_section_scripts (objfile, section_name, p, p + data.size ());
+ source_section_scripts (objfile, section_name, p, p + data.size (),
+ from_objfile_load);
}
}
@@ -1196,10 +1220,17 @@ auto_load_section_scripts (struct objfile *objfile, const char *section_name)
Two flavors of auto-loaded scripts are supported.
1) based on the path to the objfile
- 2) from .debug_gdb_scripts section */
+ 2) from .debug_gdb_scripts section
+
+ This function is called either when OBJFILE is loaded by GDB, or when the
+ user runs add-auto-load-safe-path to retry scripts that were previously
+ blocked by the safe-path. FROM_OBJFILE_LOAD distinguishes the two cases:
+ 1) true: the objfile was just loaded, scripts are always executed if safe.
+ 2) false: add-auto-load-safe-path was run, scripts that were already loaded
+ are skipped to avoid re-executing them. */
void
-load_auto_scripts_for_objfile (struct objfile &objfile)
+load_auto_scripts_for_objfile (struct objfile &objfile, bool from_objfile_load)
{
/* Return immediately if auto-loading has been globally disabled.
This is to handle sequencing of operations during gdb startup.
@@ -1212,10 +1243,10 @@ load_auto_scripts_for_objfile (struct objfile &objfile)
/* Load any extension language scripts for this objfile.
E.g., foo-gdb.gdb, foo-gdb.py. */
- auto_load_ext_lang_scripts_for_objfile (&objfile);
+ auto_load_ext_lang_scripts_for_objfile (&objfile, from_objfile_load);
/* Load any scripts mentioned in AUTO_SECTION_NAME (.debug_gdb_scripts). */
- auto_load_section_scripts (&objfile, AUTO_SECTION_NAME);
+ auto_load_section_scripts (&objfile, AUTO_SECTION_NAME, from_objfile_load);
}
/* Collect scripts to be printed in a vec. */
@@ -1567,9 +1598,9 @@ INIT_GDB_FILE (auto_load)
python_name_help, guile_name_help;
const char *suffix;
- gdb::observers::new_objfile.attach (load_auto_scripts_for_objfile,
- auto_load_new_objfile_observer_token,
- "auto-load");
+ gdb::observers::new_objfile.attach ([] (struct objfile &objfile)
+ { load_auto_scripts_for_objfile (objfile); },
+ auto_load_new_objfile_observer_token, "auto-load");
gdb::observers::all_objfiles_removed.attach (clear_section_scripts,
"auto-load");
add_setshow_boolean_cmd ("gdb-scripts", class_support,
diff --git a/gdb/auto-load.h b/gdb/auto-load.h
index d0da69373c3..26d364efb77 100644
--- a/gdb/auto-load.h
+++ b/gdb/auto-load.h
@@ -58,8 +58,10 @@ extern gdb::observers::token auto_load_new_objfile_observer_token;
extern struct auto_load_pspace_info *
get_auto_load_pspace_data_for_loading (struct program_space *pspace);
extern void auto_load_objfile_script (struct objfile *objfile,
- const struct extension_language_defn *);
-extern void load_auto_scripts_for_objfile (struct objfile &objfile);
+ const struct extension_language_defn *,
+ bool from_objfile_load = true);
+extern void load_auto_scripts_for_objfile (struct objfile &objfile,
+ bool from_objfile_load = true);
extern char auto_load_info_scripts_pattern_nl[];
extern void auto_load_info_scripts (program_space *pspace, const char *pattern,
int from_tty,
@@ -70,14 +72,15 @@ extern struct cmd_list_element **auto_load_show_cmdlist_get (void);
extern struct cmd_list_element **auto_load_info_cmdlist_get (void);
/* Return true if FILENAME is located in one of the directories of
- AUTO_LOAD_SAFE_PATH. Otherwise call warning and return false. FILENAME does
- not have to be an absolute path.
+ AUTO_LOAD_SAFE_PATH. Otherwise call warning if PRINT_WARNING and return
+ false. FILENAME does not have to be an absolute path.
Existence of FILENAME is not checked. Function will still give a warning
even if the caller would quietly skip non-existing file in unsafe
directory. */
-extern bool file_is_auto_load_safe (const char *filename);
+extern bool file_is_auto_load_safe (const char *filename,
+ bool print_warning = true);
/* Return true if auto-loading gdb scripts is enabled. */
diff --git a/gdb/extension.c b/gdb/extension.c
index d8ef8123ab5..c5cdad4c9f7 100644
--- a/gdb/extension.c
+++ b/gdb/extension.c
@@ -372,17 +372,18 @@ eval_ext_lang_from_control_command (struct command_line *cmd)
OBJFILE-gdb.gdb. */
void
-auto_load_ext_lang_scripts_for_objfile (struct objfile *objfile)
+auto_load_ext_lang_scripts_for_objfile (struct objfile *objfile,
+ bool from_objfile_load)
{
const struct extension_language_defn *gdb = &extension_language_gdb;
if (ext_lang_auto_load_enabled (gdb))
- auto_load_objfile_script (objfile, gdb);
+ auto_load_objfile_script (objfile, gdb, from_objfile_load);
for (const struct extension_language_defn *extlang : extension_languages)
{
if (extlang->ops != nullptr
&& ext_lang_auto_load_enabled (extlang))
- auto_load_objfile_script (objfile, extlang);
+ auto_load_objfile_script (objfile, extlang, from_objfile_load);
}
}
\f
diff --git a/gdb/extension.h b/gdb/extension.h
index 38a2ca5f887..82456bd2527 100644
--- a/gdb/extension.h
+++ b/gdb/extension.h
@@ -289,7 +289,8 @@ extern void ext_lang_shutdown ();
extern void eval_ext_lang_from_control_command (struct command_line *cmd);
-extern void auto_load_ext_lang_scripts_for_objfile (struct objfile *);
+extern void auto_load_ext_lang_scripts_for_objfile (struct objfile *,
+ bool from_objfile_load = true);
extern gdb::unique_xmalloc_ptr<char> apply_ext_lang_type_printers
(struct ext_lang_type_printers *, struct type *);
diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
index cf0bdb80c51..63d070d1b20 100644
--- a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
+++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
@@ -17,7 +17,9 @@
# defined in a Python script that is autoloaded are registered when an event
# handler for the new_objfile event is called. It also tests that scripts
# which were not loaded because the auto-load safe-path did not cover them are
-# loaded on the next run once the safe-path is widened.
+# loaded on the next run once the safe-path is widened. Finally, it tests that
+# add-auto-load-safe-path immediately retries previously blocked scripts without
+# requiring a re-run.
load_lib gdb-python.exp
@@ -144,6 +146,33 @@ proc newobjfile_event_test { variant } {
gdb_breakpoint [gdb_get_line_number "break to inspect"]
gdb_test "continue" "Breakpoint $decimal, main .*"
gdb_test "print test" "MyClassTestLib object, id: 1.*"
+
+ # Test that add-auto-load-safe-path immediately retries previously blocked
+ # scripts without requiring a re-run.
+ clean_restart
+ gdb_load ${binfile}
+ gdb_load_shlib $binfile_lib
+
+ gdb_test_no_output "set auto-load safe-path /restricted" \
+ "set restricted auto-load safe-path for refresh"
+
+ if { ![runto_main] } {
+ return
+ }
+
+ gdb_test_no_output \
+ "add-auto-load-safe-path ${safe_path_allow}" \
+ "add auto-load safe-path"
+
+ gdb_test_multiple "info auto-load python-scripts" \
+ "verify script loaded after refresh" {
+ -re -wrap "Yes.*${script_name}.*" {
+ pass $gdb_test_name
+ }
+ -re -wrap "No.*${script_name}.*" {
+ fail $gdb_test_name
+ }
+ }
}
}
--
2.43.0
prev parent reply other threads:[~2026-09-03 9:26 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-01 14:06 [PATCH] [gdb] Fix auto-load script re-execution on subsequent runs Tudor-Stefan Magirescu
2026-09-03 9:21 ` [PATCH v2 0/2] Fix auto-load script loading after safe-path is widened Tudor-Stefan Magirescu
2026-09-03 9:22 ` [PATCH v2 1/2] [gdb] Fix auto-load script re-execution on subsequent runs Tudor-Stefan Magirescu
2026-09-03 9:22 ` Tudor-Stefan Magirescu [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260903092201.663063-3-tudor.magirescu@gmail.com \
--to=tudor.magirescu@gmail.com \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox