From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5913 invoked by alias); 24 Mar 2012 18:40:14 -0000 Received: (qmail 5902 invoked by uid 22791); 24 Mar 2012 18:40:11 -0000 X-SWARE-Spam-Status: No, hits=-6.6 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_BJ,TW_CP,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 24 Mar 2012 18:39:54 +0000 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q2OIdslf028990 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Sat, 24 Mar 2012 14:39:54 -0400 Received: from host2.jankratochvil.net (ovpn-116-28.ams2.redhat.com [10.36.116.28]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id q2OIdkdt006775 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO) for ; Sat, 24 Mar 2012 14:39:48 -0400 Date: Sat, 24 Mar 2012 18:40:00 -0000 From: Jan Kratochvil To: gdb-patches@sourceware.org Subject: [patch 4/6] set auto-load safe-path Message-ID: <20120324183946.GE26662@host2.jankratochvil.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-03/txt/msg00830.txt.bz2 Hi, this configure parameter is intended for distros: ./configure --with-auto-load-safe-path=/usr:/bin:/sbin:/lib:/lib64 Some distros will add /opt. There must not be directories like /home, /tmp. Thanks, Jan gdb/ 2012-03-20 Jan Kratochvil * NEWS: New commands "set auto-load safe-path" and "show auto-load safe-path". * auto-load.c: Include gdb_vecs.h. (auto_load_safe_path, auto_load_safe_path_vec) (auto_load_safe_path_vec_update, set_auto_load_safe_path) (show_auto_load_safe_path, filename_is_in_dir) (filename_is_in_auto_load_safe_path_vec, file_is_auto_load_safe): New. (source_gdb_script_for_objfile): New variable is_safe. Call file_is_auto_load_safe. Return if it is not. (struct loaded_script): New field loaded. (maybe_add_script): Add parameter loaded. Initialize SLOT with it. (print_script): Use LOADED indicator instead of FULL_PATH. Change output "Missing" to "No". (_initialize_auto_load): Initialize auto_load_safe_path. Register "set auto-load safe-path". * auto-load.h (maybe_add_script): Add parameter loaded. (file_is_auto_load_safe): New declaration. * config.in: Regenerate. * configure: Regenerate. * configure.ac: New parameters --with-auto-load-safe-path and --without-auto-load-safe-path. * linux-thread-db.c (try_thread_db_load_from_pdir_1) (try_thread_db_load_from_dir): Check file_is_auto_load_safe first. * main.c (captured_main): Check file_is_auto_load_safe for LOCAL_GDBINIT. * python/py-auto-load.c (gdbpy_load_auto_script_for_objfile): New variable is_safe. Call file_is_auto_load_safe. Return if it is not. (source_section_scripts): Call file_is_auto_load_safe. Return if it is not. gdb/doc/ 2012-03-24 Jan Kratochvil * gdb.texinfo (Startup): New @anchor local-gdbinit. (Auto-loading): New menu item for auto-load safe-path. Update the "Missing" output to "No." (auto-load safe-path): New node. gdb/testsuite/ 2012-03-24 Jan Kratochvil * gdb.python/py-objfile-script.exp (set auto-load safe-path): New. * gdb.python/py-section-script.exp (set auto-load safe-path): New. --- a/gdb/NEWS +++ b/gdb/NEWS @@ -142,6 +142,10 @@ set auto-load libthread-db on|off show auto-load libthread-db Controls auto-loading of inferior specific thread debugging shared library. +set auto-load safe-path [:...] +show auto-load safe-path + Sets a list of directories safe to hold auto-loaded files. + * New remote packets z0/z1 conditional breakpoints extension --- a/gdb/auto-load.c +++ b/gdb/auto-load.c @@ -32,6 +32,7 @@ #include "gdbcmd.h" #include "cli/cli-decode.h" #include "cli/cli-setshow.h" +#include "gdb_vecs.h" /* The suffix of per-objfile scripts to auto-load as non-Python command files. E.g. When the program loads libfoo.so, look for libfoo-gdb.rc. */ @@ -113,6 +114,151 @@ show_auto_load_local_gdbinit (struct ui_file *file, int from_tty, value); } +/* Directory list safe to hold auto-loaded files. It is not checked for + absolute paths but they are strongly recommended. It is initialized by + _initialize_auto_load. */ +static char *auto_load_safe_path; + +/* Vector of directory elements of AUTO_LOAD_SAFE_PATH with each one normalized + by gdb_realpath. */ +static VEC (char_ptr) *auto_load_safe_path_vec; + +/* Update auto_load_safe_path_vec from current AUTO_LOAD_SAFE_PATH. */ + +static void +auto_load_safe_path_vec_update (void) +{ + char *safe_path, *filename_real = NULL, *dir; + int ix; + + for (ix = 0; VEC_iterate (char_ptr, auto_load_safe_path_vec, ix, dir); ++ix) + xfree (dir); + VEC_free (char_ptr, auto_load_safe_path_vec); + auto_load_safe_path_vec = NULL; + + safe_path = alloca (strlen (auto_load_safe_path) + 1); + strcpy (safe_path, auto_load_safe_path); + + do + { + char *next_dir, *real_path; + + next_dir = strchr (safe_path, DIRNAME_SEPARATOR); + if (next_dir != NULL) + *next_dir++ = 0; + + real_path = gdb_realpath (safe_path); + VEC_safe_push (char_ptr, auto_load_safe_path_vec, real_path); + + safe_path = next_dir; + } + while (safe_path != NULL); +} + +/* "set" command for the auto_load_safe_path configuration variable. */ + +static void +set_auto_load_safe_path (char *args, int from_tty, struct cmd_list_element *c) +{ + auto_load_safe_path_vec_update (); +} + +/* "show" command for the auto_load_safe_path configuration variable. */ + +static void +show_auto_load_safe_path (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, + _("Directory list safe to hold auto-loaded files is %s.\n"), + value); +} + +/* Return 1 if FILENAME is equal to DIR or if FILENAME belongs to the + subdirectory DIR. Return 0 otherwise. gdb_realpath normalization is never + done here. */ + +static ATTRIBUTE_PURE int +filename_is_in_dir (const char *filename, const char *dir) +{ + size_t dir_len = strlen (dir); + + while (dir_len && IS_DIR_SEPARATOR (dir[dir_len - 1])) + dir_len--; + + return (filename_ncmp (dir, filename, dir_len) == 0 + && (IS_DIR_SEPARATOR (filename[dir_len]) + || filename[dir_len] == '\0')); +} + +/* Return 1 if FILENAME belongs to one of directory components of + AUTO_LOAD_SAFE_PATH_VEC. Return 0 otherwise. + auto_load_safe_path_vec_update is never called. + *FILENAME_REALP may be updated by gdb_realpath of FILENAME - it has to be + freed by the caller. */ + +static int +filename_is_in_auto_load_safe_path_vec (const char *filename, + char **filename_realp) +{ + char *dir; + int ix; + + for (ix = 0; VEC_iterate (char_ptr, auto_load_safe_path_vec, ix, dir); ++ix) + { + if (*filename_realp == NULL && filename_is_in_dir (filename, dir)) + break; + + if (*filename_realp == NULL) + *filename_realp = gdb_realpath (filename); + + if (filename_is_in_dir (*filename_realp, dir)) + break; + } + + if (dir != NULL) + return 1; + + return 0; +} + +/* Return 1 if FILENAME is located in one of the directories of + AUTO_LOAD_SAFE_PATH. Otherwise call warning and return 0. 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. */ + +int +file_is_auto_load_safe (const char *filename) +{ + char *filename_real = NULL; + struct cleanup *back_to; + + back_to = make_cleanup (free_current_contents, &filename_real); + + if (filename_is_in_auto_load_safe_path_vec (filename, &filename_real)) + { + do_cleanups (back_to); + return 1; + } + + auto_load_safe_path_vec_update (); + if (filename_is_in_auto_load_safe_path_vec (filename, &filename_real)) + { + do_cleanups (back_to); + return 1; + } + + warning (_("File \"%s\" auto-loading has been declined by your " + "`auto-load safe-path' set to \"%s\"."), + filename_real, auto_load_safe_path); + + do_cleanups (back_to); + return 0; +} + /* Definition of script language for GDB canned sequences of commands. */ static const struct script_language script_language_gdb @@ -122,13 +268,20 @@ static void source_gdb_script_for_objfile (struct objfile *objfile, FILE *file, const char *filename) { + int is_safe; struct auto_load_pspace_info *pspace_info; volatile struct gdb_exception e; + is_safe = file_is_auto_load_safe (filename); + /* Add this script to the hash table too so "info auto-load gdb-scripts" can print it. */ pspace_info = get_auto_load_pspace_data_for_loading (current_program_space); - maybe_add_script (pspace_info, filename, filename, &script_language_gdb); + maybe_add_script (pspace_info, is_safe, filename, filename, + &script_language_gdb); + + if (!is_safe) + return; TRY_CATCH (e, RETURN_MASK_ALL) { @@ -163,6 +316,9 @@ struct loaded_script inaccessible). */ const char *full_path; + /* Non-zero if this script has been loaded. */ + int loaded; + const struct script_language *language; }; @@ -255,12 +411,13 @@ get_auto_load_pspace_data_for_loading (struct program_space *pspace) return info; } -/* Add script NAME in LANGUAGE to hash table of PSPACE_INFO. - FULL_PATH is NULL if the script wasn't found. The result is +/* Add script NAME in LANGUAGE to hash table of PSPACE_INFO. LOADED 1 if the + script has been (is going to) be loaded, 0 otherwise (such as if it has not + been found). FULL_PATH is NULL if the script wasn't found. The result is true if the script was already in the hash table. */ int -maybe_add_script (struct auto_load_pspace_info *pspace_info, +maybe_add_script (struct auto_load_pspace_info *pspace_info, int loaded, const char *name, const char *full_path, const struct script_language *language) { @@ -294,6 +451,7 @@ maybe_add_script (struct auto_load_pspace_info *pspace_info, } else (*slot)->full_path = NULL; + (*slot)->loaded = loaded; (*slot)->language = language; } @@ -455,7 +613,7 @@ print_script (struct loaded_script *script) chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); - ui_out_field_string (uiout, "loaded", script->full_path ? "Yes" : "Missing"); + ui_out_field_string (uiout, "loaded", script->loaded ? "Yes" : "No"); ui_out_field_string (uiout, "script", script->name); ui_out_text (uiout, "\n"); @@ -745,4 +903,21 @@ This options has security implications for untrusted inferiors."), _("Print whether current directory .gdbinit file has been loaded.\n\ Usage: info auto-load local-gdbinit"), auto_load_info_cmdlist_get ()); + + auto_load_safe_path = xstrdup (DEFAULT_AUTO_LOAD_SAFE_PATH); + auto_load_safe_path_vec_update (); + add_setshow_optional_filename_cmd ("safe-path", class_support, + &auto_load_safe_path, _("\ +Set the directories safe to hold auto-loaded files."), _("\ +Show the directories safe to hold auto-loaded files."), _("\ +Various files loaded automatically for the 'set auto-load ...' options must\n\ +be located in one of the directories listed by this option. Warning will be\n\ +printed and file will not be used otherwise. Use empty string to allow any\n\ +file for the 'set auto-load ...' options. This option is ignored for the\n\ +kinds of files having 'set auto-load ... off'.\n\ +This options has security implications for untrusted inferiors."), + set_auto_load_safe_path, + show_auto_load_safe_path, + auto_load_set_cmdlist_get (), + auto_load_show_cmdlist_get ()); } --- a/gdb/auto-load.h +++ b/gdb/auto-load.h @@ -43,7 +43,8 @@ extern int auto_load_local_gdbinit_loaded; extern struct auto_load_pspace_info * get_auto_load_pspace_data_for_loading (struct program_space *pspace); extern int maybe_add_script (struct auto_load_pspace_info *pspace_info, - const char *name, const char *full_path, + int loaded, const char *name, + const char *full_path, const struct script_language *language); extern void auto_load_objfile_script (struct objfile *objfile, const struct script_language *language); @@ -57,4 +58,6 @@ extern struct cmd_list_element **auto_load_set_cmdlist_get (void); extern struct cmd_list_element **auto_load_show_cmdlist_get (void); extern struct cmd_list_element **auto_load_info_cmdlist_get (void); +extern int file_is_auto_load_safe (const char *filename); + #endif /* AUTO_LOAD_H */ --- a/gdb/config.in +++ b/gdb/config.in @@ -43,6 +43,9 @@ moved. */ #undef DEBUGDIR_RELOCATABLE +/* Directories safe to hold auto-loaded files. */ +#undef DEFAULT_AUTO_LOAD_SAFE_PATH + /* Define to BFD's default architecture. */ #undef DEFAULT_BFD_ARCH --- a/gdb/configure +++ b/gdb/configure @@ -951,6 +951,7 @@ enable_dependency_tracking with_separate_debug_dir with_gdb_datadir with_relocated_sources +with_auto_load_safe_path enable_targets enable_64_bit_bfd enable_gdbcli @@ -1659,6 +1660,10 @@ Optional Packages: [DATADIR/gdb] --with-relocated-sources=PATH automatically relocate this path for source files + --with-auto-load-safe-path=PATH + directories safe to hold auto-loaded files + --without-auto-load-safe-path + do not restrict auto-loaded files locations --with-libunwind-ia64 use libunwind frame unwinding for ia64 targets --with-curses use the curses library instead of the termcap library @@ -7940,6 +7945,32 @@ _ACEOF fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for default auto-load safe-path" >&5 +$as_echo_n "checking for default auto-load safe-path... " >&6; } + +# Check whether --with-auto-load-safe-path was given. +if test "${with_auto_load_safe_path+set}" = set; then : + withval=$with_auto_load_safe_path; if test "$with_auto_load_safe_path" = "no"; then + with_auto_load_safe_path="" + fi +else + with_auto_load_safe_path="$prefix" +fi + + + test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + ac_define_dir=`eval echo $with_auto_load_safe_path` + ac_define_dir=`eval echo $ac_define_dir` + +cat >>confdefs.h <<_ACEOF +#define DEFAULT_AUTO_LOAD_SAFE_PATH "$ac_define_dir" +_ACEOF + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_auto_load_safe_path" >&5 +$as_echo "$with_auto_load_safe_path" >&6; } + subdirs="$subdirs testsuite" --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -134,6 +134,18 @@ AS_HELP_STRING([--with-relocated-sources=PATH], [automatically relocate this pat [Relocated directory for source files. ]) ]) +AC_MSG_CHECKING([for default auto-load safe-path]) +AC_ARG_WITH(auto-load-safe-path, +AS_HELP_STRING([--with-auto-load-safe-path=PATH], [directories safe to hold auto-loaded files]) +AS_HELP_STRING([--without-auto-load-safe-path], [do not restrict auto-loaded files locations]), +[if test "$with_auto_load_safe_path" = "no"; then + with_auto_load_safe_path="" + fi], +[with_auto_load_safe_path="$prefix"]) +AC_DEFINE_DIR(DEFAULT_AUTO_LOAD_SAFE_PATH, with_auto_load_safe_path, + [Directories safe to hold auto-loaded files.]) +AC_MSG_RESULT([$with_auto_load_safe_path]) + AC_CONFIG_SUBDIRS(testsuite) # Check whether to support alternative target configurations --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -1280,6 +1280,7 @@ that file. @item Processes command line options and operands. +@anchor{local-gdbinit} @item Reads and executes the commands from init file (if any) in the current working directory as long as @samp{set auto-load local-gdbinit} is set to @@ -24751,6 +24752,7 @@ and @code{.debug_gdb_scripts} section. * objfile-gdb.rc file:: The @file{@var{objfile}-gdb.rc} file * .debug_gdb_scripts section:: The @code{.debug_gdb_scripts} section * Which flavor to choose?:: +* auto-load safe-path:: Security restriction for auto-loading @end menu The auto-loading feature is useful for supplying application-specific @@ -24808,10 +24810,10 @@ Example: @smallexample (gdb) info auto-load python-scripts -Loaded Script -Yes py-section-script.py - full name: /tmp/py-section-script.py -Missing my-foo-pretty-printers.py +Loaded Script +Yes py-section-script.py + full name: /tmp/py-section-script.py +No my-foo-pretty-printers.py @end smallexample @end table @@ -24960,6 +24962,39 @@ cumbersome. It may be easier to specify the scripts in the top of the source tree to the source search path. @end itemize +@node auto-load safe-path +@subsubsection Security restriction for auto-loading +@cindex auto-load safe-path + +As the files of inferior can come from untrusted source (such as submitted by +an application user) @value{GDBN} does not always load any files automatically. + +This setting should contain so called canonical filenames, after any symbolic +links, current and parent directories have been resolved. Both the +@samp{auto-load safe-path} setting and the scripts being verified are +canonicalized first for their matching. + +@table @code +@kindex set auto-load safe-path +@item set auto-load safe-path @var{directories} +Set the list of directories (and their subdirectories) trusted for automatic +loading and execution of scripts. The list of directories uses directory +separator as its delimiter. You can also enter a specific trusted file. + +@kindex show auto-load safe-path +@item show auto-load safe-path +Show the list of directories (and their subdirectories) trusted for automatic +loading and execution of scripts. +@end table + +Setting this variable to an empty string disables this security protection. +This variable is supposed to be set to the system directories writable by the +system superuser only. Users can add their source directories in home +directories. See also the deprecated option @xref{local-gdbinit}. + +Be aware even downloaded source packages may contain exploit code which may get +executed by @value{GDBN} without explicitly running any program therein. + @node Python modules @subsection Python modules @cindex python modules --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -869,7 +869,11 @@ try_thread_db_load_from_pdir_1 (struct objfile *obj) /* This should at minimum hit the first character. */ gdb_assert (cp != NULL); strcpy (cp + 1, LIBTHREAD_DB_SO); - result = try_thread_db_load (path); + + if (!file_is_auto_load_safe (path)) + result = 0; + else + result = try_thread_db_load (path); do_cleanups (cleanup); return result; @@ -935,7 +939,11 @@ try_thread_db_load_from_dir (const char *dir, size_t dir_len) memcpy (path, dir, dir_len); path[dir_len] = '/'; strcpy (path + dir_len + 1, LIBTHREAD_DB_SO); - result = try_thread_db_load (path); + + if (!file_is_auto_load_safe (path)) + result = 0; + else + result = try_thread_db_load (path); do_cleanups (cleanup); return result; --- a/gdb/main.c +++ b/gdb/main.c @@ -1002,7 +1002,7 @@ captured_main (void *data) warning (_("Ignoring file .gdbinit in current directory as it " "has been deprecated. %s"), _(adv)); - else + else if (file_is_auto_load_safe (local_gdbinit)) { auto_load_local_gdbinit_loaded = 1; catch_command_errors (source_script, local_gdbinit, 0, --- a/gdb/python/py-auto-load.c +++ b/gdb/python/py-auto-load.c @@ -72,14 +72,19 @@ static void gdbpy_load_auto_script_for_objfile (struct objfile *objfile, FILE *file, const char *filename) { + int is_safe; struct auto_load_pspace_info *pspace_info; + is_safe = file_is_auto_load_safe (filename); + /* Add this script to the hash table too so "info auto-load python-scripts" can print it. */ pspace_info = get_auto_load_pspace_data_for_loading (current_program_space); - maybe_add_script (pspace_info, filename, filename, &script_language_python); + maybe_add_script (pspace_info, is_safe, filename, filename, + &script_language_python); - source_python_script_for_objfile (objfile, file, filename); + if (is_safe) + source_python_script_for_objfile (objfile, file, filename); } /* Load scripts specified in OBJFILE. @@ -147,6 +152,9 @@ source_section_scripts (struct objfile *objfile, const char *source_name, { make_cleanup_fclose (stream); make_cleanup (xfree, full_path); + + if (!file_is_auto_load_safe (full_path)) + opened = 0; } else { @@ -167,7 +175,7 @@ Use `info auto-load python [REGEXP]' to list them."), IWBN if complaints.c were more general-purpose. */ - in_hash_table = maybe_add_script (pspace_info, file, full_path, + in_hash_table = maybe_add_script (pspace_info, opened, file, full_path, &script_language_python); /* If this file is not currently loaded, load it. */ --- a/gdb/testsuite/gdb.python/py-objfile-script.exp +++ b/gdb/testsuite/gdb.python/py-objfile-script.exp @@ -37,6 +37,7 @@ if { [skip_python_tests] } { continue } set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}-gdb.py.in ${subdir}/${testfile}-gdb.py] gdb_reinitialize_dir $srcdir/$subdir +gdb_test_no_output "set auto-load safe-path ${remote_python_file}" "set auto-load safe-path" gdb_load ${binfile} # Verify gdb loaded the script. --- a/gdb/testsuite/gdb.python/py-section-script.exp +++ b/gdb/testsuite/gdb.python/py-section-script.exp @@ -49,6 +49,7 @@ if { [skip_python_tests] } { continue } set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py] gdb_reinitialize_dir $srcdir/$subdir +gdb_test_no_output "set auto-load safe-path ${remote_python_file}" "set auto-load safe-path" gdb_load ${binfile} # Verify gdb loaded the script.