From dbc93d61958d966757d142cef4dcc10e90b0d44c Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Fri, 13 Dec 2019 13:52:58 +0100 Subject: [PATCH] Avoid throwing exceptions during early boot if stdin is closed * libguile/fports.c (scm_i_fdes_is_valid): New internal helper. (scm_i_fdes_to_port): Use new helper. * libguile/fports.h: Declare new helper. * libguile/init.c (scm_standard_stream_to_port): Refactor to use scm_i_fdes_is_valid. --- libguile/fports.c | 44 +++++++++++++++++++++++++++----------------- libguile/fports.h | 4 +++- libguile/init.c | 44 +++++++------------------------------------- 3 files changed, 37 insertions(+), 55 deletions(-) diff --git a/libguile/fports.c b/libguile/fports.c index d97c54c01..6019d9ec3 100644 --- a/libguile/fports.c +++ b/libguile/fports.c @@ -401,6 +401,29 @@ SCM_DEFINE (scm_i_open_file, "open-file", 2, 0, 1, /* Building Guile ports from a file descriptor. */ +int +scm_i_fdes_is_valid (int fdes, long mode_bits) +{ +#ifdef F_GETFL + int flags = fcntl (fdes, F_GETFL, 0); + if (flags == -1) + return 0; + flags &= O_ACCMODE; + if (flags == O_RDWR) + return 1; + if (flags != O_WRONLY && (mode_bits & SCM_WRTNG)) + return 0; + if (flags != O_RDONLY && (mode_bits & SCM_RDNG)) + return 0; + return 1; +#else + /* If we don't have F_GETFL, as on mingw, at least we can test that + it is a valid file descriptor. */ + struct stat st; + return fstat (fdes, &st) == 0; +#endif +} + /* Build a Scheme port from an open file descriptor `fdes'. MODE indicates whether FILE is open for reading or writing; it uses the same notation as open-file's second argument. @@ -415,27 +438,14 @@ scm_i_fdes_to_port (int fdes, long mode_bits, SCM name, unsigned options) if (options & SCM_FPORT_OPTION_VERIFY) { - /* Check that the foreign FD is valid and matches the mode - bits. */ -#ifdef F_GETFL - int flags = fcntl (fdes, F_GETFL, 0); - if (flags == -1) - SCM_SYSERROR; - flags &= O_ACCMODE; - if (flags != O_RDWR - && ((flags != O_WRONLY && (mode_bits & SCM_WRTNG)) - || (flags != O_RDONLY && (mode_bits & SCM_RDNG)))) + errno = 0; + if (!scm_i_fdes_is_valid (fdes, mode_bits)) { + if (errno) + SCM_SYSERROR; SCM_MISC_ERROR ("requested file mode not available on fdes", SCM_EOL); } -#else - /* If we don't have F_GETFL, as on mingw, at least we can test that - it is a valid file descriptor. */ - struct stat st; - if (fstat (fdes, &st) != 0) - SCM_SYSERROR; -#endif } fp = (scm_t_fport *) scm_gc_malloc_pointerless (sizeof (scm_t_fport), diff --git a/libguile/fports.h b/libguile/fports.h index fd5b86d96..3a895775f 100644 --- a/libguile/fports.h +++ b/libguile/fports.h @@ -1,7 +1,7 @@ #ifndef SCM_FPORTS_H #define SCM_FPORTS_H -/* Copyright 1995-2001,2006,2008-2009,2011-2012,2017-2018 +/* Copyright 1995-2001,2006,2008-2009,2011-2012,2017-2019 Free Software Foundation, Inc. This file is part of Guile. @@ -86,8 +86,10 @@ enum scm_fport_option that case. */ SCM_FPORT_OPTION_NOT_SEEKABLE = 1U<<1 }; +SCM_INTERNAL int scm_i_fdes_is_valid (int fdes, long mode_bits); SCM_INTERNAL SCM scm_i_fdes_to_port (int fdes, long mode_bits, SCM name, unsigned options); + #endif /* BUILDING_LIBGUILE */ #endif /* SCM_FPORTS_H */ diff --git a/libguile/init.c b/libguile/init.c index 504c28831..2a9f963ac 100644 --- a/libguile/init.c +++ b/libguile/init.c @@ -156,49 +156,19 @@ /* initializing standard and current I/O ports */ -typedef struct -{ - int fdes; - char *mode; -} stream_body_data; - -/* proc to be called in scope of exception handler stream_handler. */ -static SCM -stream_body (void *data) -{ - stream_body_data *body_data = (stream_body_data *) data; - SCM port = scm_fdes_to_port (body_data->fdes, body_data->mode, SCM_BOOL_F); - scm_set_port_revealed_x (port, SCM_INUM1); - return port; -} - -/* exception handler for stream_body. */ -static SCM -stream_handler (void *data SCM_UNUSED, - SCM tag SCM_UNUSED, - SCM throw_args SCM_UNUSED) -{ - return SCM_BOOL_F; -} - /* Convert a file descriptor to a port, using scm_fdes_to_port. - set the revealed count for FILE's file descriptor to 1, so - that fdes won't be closed when the port object is GC'd. - - catch exceptions: allow Guile to be able to start up even - if it has been handed bogus stdin/stdout/stderr. replace the - bad ports with void ports. */ + that fdes won't be closed when the port object is GC'd. */ static SCM scm_standard_stream_to_port (int fdes, char *mode) { - SCM port; - stream_body_data body_data; + long mode_bits = scm_mode_bits (mode); - body_data.fdes = fdes; - body_data.mode = mode; - port = scm_internal_catch (SCM_BOOL_T, stream_body, &body_data, - stream_handler, NULL); - if (scm_is_false (port)) - port = scm_void_port (mode); + if (!scm_i_fdes_is_valid (fdes, mode_bits)) + return scm_void_port (mode); + + SCM port = scm_i_fdes_to_port (fdes, mode_bits, SCM_BOOL_F, 0); + scm_set_port_revealed_x (port, SCM_INUM1); return port; }