The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=e01fe14c9aac95b1daec877af1729545b6d1b147
commit e01fe14c9aac95b1daec877af1729545b6d1b147 Author: John Baldwin <j...@freebsd.org> AuthorDate: 2025-08-04 19:38:06 +0000 Commit: John Baldwin <j...@freebsd.org> CommitDate: 2025-08-04 19:38:06 +0000 ctld: Make config file parsing exception safe Split parse_conf in half keeping yyparse_conf as C-only function in parse.y. parse_conf uses freebsd::FILE_up to ensure that the config file is always closed. Both parse_conf and uclparse_conf catch any exceptions thrown during parsing. This is in preparation for using C++ objects allocated with new for various data structures. Note that this treats memory allocation failures from new as parsing errors rather than ctld exiting entirely as it currently does if malloc or calloc fail. Sponsored by: Chelsio Communications Pull Request: https://github.com/freebsd/freebsd-src/pull/1794 --- usr.sbin/ctld/Makefile | 1 + usr.sbin/ctld/conf.cc | 25 +++++++++++++++++++++++++ usr.sbin/ctld/conf.h | 2 +- usr.sbin/ctld/ctld.hh | 1 + usr.sbin/ctld/parse.y | 10 ++-------- usr.sbin/ctld/uclparse.cc | 12 +++++++++++- 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile index 314554a99b56..67f89dca2757 100644 --- a/usr.sbin/ctld/Makefile +++ b/usr.sbin/ctld/Makefile @@ -12,6 +12,7 @@ CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -I${SRCTOP}/sys/cam/ctl CFLAGS+= -I${SRCTOP}/sys/dev/iscsi CFLAGS+= -I${SRCTOP}/lib/libiscsiutil +CFLAGS+= -I${SRCTOP}/lib/libutil++ #CFLAGS+= -DICL_KERNEL_PROXY NO_WCAST_ALIGN= CXXWARNFLAGS.gcc= -Wno-shadow diff --git a/usr.sbin/ctld/conf.cc b/usr.sbin/ctld/conf.cc index 1750f7ab96c6..c9e1a2fb9968 100644 --- a/usr.sbin/ctld/conf.cc +++ b/usr.sbin/ctld/conf.cc @@ -39,6 +39,8 @@ #include <string.h> #include <cam/scsi/scsi_all.h> +#include <libutil++.hh> + #include "conf.h" #include "ctld.hh" @@ -757,3 +759,26 @@ target_start_lun(u_int id) lun = new_lun; return (true); } + +bool +parse_conf(const char *path) +{ + freebsd::FILE_up fp(fopen(path, "r")); + if (fp == nullptr) { + log_warn("unable to open configuration file %s", path); + return (false); + } + + bool parsed; + try { + parsed = yyparse_conf(fp.get()); + } catch (std::bad_alloc) { + log_warnx("failed to allocate memory parsing %s", path); + return (false); + } catch (...) { + log_warnx("unknown exception parsing %s", path); + return (false); + } + + return (parsed); +} diff --git a/usr.sbin/ctld/conf.h b/usr.sbin/ctld/conf.h index 6e287ce70436..b13fd80e9fe5 100644 --- a/usr.sbin/ctld/conf.h +++ b/usr.sbin/ctld/conf.h @@ -97,7 +97,7 @@ bool lun_set_path(const char *value); bool lun_set_serial(const char *value); bool lun_set_size(uint64_t value); -bool parse_conf(const char *path); +bool yyparse_conf(FILE *fp); __END_DECLS diff --git a/usr.sbin/ctld/ctld.hh b/usr.sbin/ctld/ctld.hh index b1757f98ac81..61e453d8dc23 100644 --- a/usr.sbin/ctld/ctld.hh +++ b/usr.sbin/ctld/ctld.hh @@ -247,6 +247,7 @@ struct ctld_connection { extern int ctl_fd; +bool parse_conf(const char *path); bool uclparse_conf(const char *path); struct conf *conf_new(void); diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y index c0146262e895..432183ed794c 100644 --- a/usr.sbin/ctld/parse.y +++ b/usr.sbin/ctld/parse.y @@ -890,20 +890,14 @@ yyerror(const char *str) } bool -parse_conf(const char *path) +yyparse_conf(FILE *fp) { int error; - yyin = fopen(path, "r"); - if (yyin == NULL) { - log_warn("unable to open configuration file %s", path); - return (false); - } - + yyin = fp; lineno = 1; yyrestart(yyin); error = yyparse(); - fclose(yyin); return (error == 0); } diff --git a/usr.sbin/ctld/uclparse.cc b/usr.sbin/ctld/uclparse.cc index 8788e1cbb981..4c7bbb43d75d 100644 --- a/usr.sbin/ctld/uclparse.cc +++ b/usr.sbin/ctld/uclparse.cc @@ -41,6 +41,8 @@ #include <netinet/in.h> #include <netinet/ip.h> +#include <memory> + #include "conf.h" #include "ctld.hh" @@ -1113,7 +1115,15 @@ uclparse_conf(const char *path) } top = ucl_parser_get_object(parser); - parsed = uclparse_toplevel(top); + try { + parsed = uclparse_toplevel(top); + } catch (std::bad_alloc) { + log_warnx("failed to allocate memory parsing %s", path); + parsed = false; + } catch (...) { + log_warnx("unknown exception parsing %s", path); + parsed = false; + } ucl_object_unref(top); ucl_parser_free(parser);