Skip to content

Commit a88f9db

Browse files
committed
systemctl: unset const char* arguments in static destructors
When fuzzing, the following happens: - we parse 'data' and produce an argv array, - one of the items in argv is assigned to arg_host, - the argv array is subsequently freed by strv_freep(), and arg_host has a dangling symlink. In normal use, argv is static, so arg_host can never become a dangling pointer. In fuzz-systemctl-parse-argv, if we repeatedly parse the same array, we have some dangling pointers while we're in the middle of parsing. If we parse the same array a second time, at the end all the dangling pointers will have been replaced again. But for a short time, if parsing one of the arguments uses another argument, we would use a dangling pointer. Such a case occurs when we have --host=… --boot-loader-entry=help. The latter calls acquire_bus() which uses arg_host. I'm not particularly happy with making the code more complicated just for fuzzing, but I think it's better to resolve this, even if the issue cannot occur in normal invocations, than to deal with fuzzer reports. Should fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=31714.
1 parent 6b42227 commit a88f9db

File tree

6 files changed

+24
-8
lines changed

6 files changed

+24
-8
lines changed

src/basic/alloc-util.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
7979
memcpy_safe(_q_, p, _l_); \
8080
})
8181

82+
static inline void unsetp(void *p) {
83+
/* A trivial "destructor" that can be used in cases where we want to
84+
* unset a pointer from a _cleanup_ function. */
85+
86+
*(void**)p = NULL;
87+
}
88+
8289
static inline void freep(void *p) {
8390
*(void**)p = mfree(*(void**) p);
8491
}

src/systemctl/systemctl-kill.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ int kill_unit(int argc, char *argv[], void *userdata) {
2222
arg_kill_who = "all";
2323

2424
/* --fail was specified */
25-
if (streq(arg_job_mode, "fail"))
25+
if (streq(arg_job_mode(), "fail"))
2626
kill_who = strjoina(arg_kill_who, "-fail");
2727

2828
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);

src/systemctl/systemctl-start-unit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ int start_unit(int argc, char *argv[], void *userdata) {
306306
/* A command in style of "systemctl start <unit1> <unit2> …", "sysemctl stop <unit1> <unit2> …" and so on */
307307
method = verb_to_method(argv[0]);
308308
job_type = verb_to_job_type(argv[0]);
309-
mode = arg_job_mode;
309+
mode = arg_job_mode();
310310
} else
311311
method = job_type = mode = NULL;
312312

src/systemctl/systemctl.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ char **arg_states = NULL;
6565
char **arg_properties = NULL;
6666
bool arg_all = false;
6767
enum dependency arg_dependency = DEPENDENCY_FORWARD;
68-
const char *arg_job_mode = "replace";
68+
const char *_arg_job_mode = NULL;
6969
UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
7070
bool arg_wait = false;
7171
bool arg_no_block = false;
@@ -115,8 +115,13 @@ bool arg_marked = false;
115115
STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep);
116116
STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep);
117117
STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
118+
STATIC_DESTRUCTOR_REGISTER(_arg_job_mode, unsetp);
118119
STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep);
120+
STATIC_DESTRUCTOR_REGISTER(arg_kill_who, unsetp);
119121
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
122+
STATIC_DESTRUCTOR_REGISTER(arg_reboot_argument, unsetp);
123+
STATIC_DESTRUCTOR_REGISTER(arg_host, unsetp);
124+
STATIC_DESTRUCTOR_REGISTER(arg_boot_loader_entry, unsetp);
120125
STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep);
121126

122127
static int systemctl_help(void) {
@@ -598,19 +603,19 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
598603
break;
599604

600605
case ARG_JOB_MODE:
601-
arg_job_mode = optarg;
606+
_arg_job_mode = optarg;
602607
break;
603608

604609
case ARG_FAIL:
605-
arg_job_mode = "fail";
610+
_arg_job_mode = "fail";
606611
break;
607612

608613
case ARG_IRREVERSIBLE:
609-
arg_job_mode = "replace-irreversibly";
614+
_arg_job_mode = "replace-irreversibly";
610615
break;
611616

612617
case ARG_IGNORE_DEPENDENCIES:
613-
arg_job_mode = "ignore-dependencies";
618+
_arg_job_mode = "ignore-dependencies";
614619
break;
615620

616621
case ARG_USER:

src/systemctl/systemctl.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ extern char **arg_states;
4949
extern char **arg_properties;
5050
extern bool arg_all;
5151
extern enum dependency arg_dependency;
52-
extern const char *arg_job_mode;
52+
extern const char *_arg_job_mode;
5353
extern UnitFileScope arg_scope;
5454
extern bool arg_wait;
5555
extern bool arg_no_block;
@@ -96,4 +96,8 @@ extern bool arg_read_only;
9696
extern bool arg_mkdir;
9797
extern bool arg_marked;
9898

99+
static inline const char* arg_job_mode(void) {
100+
return _arg_job_mode ?: "replace";
101+
}
102+
99103
int systemctl_dispatch_parse_argv(int argc, char *argv[]);
132 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)