Skip to content

Commit edc508a

Browse files
problamebehlendorf
authored andcommitted
libzpool: set_global_var: refactor to not modify 'arg'
Also fixes leak of the dlopen handle in the error case. Reviewed-by: Matthew Ahrens <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Pavel Zakharov <[email protected]> Signed-off-by: Christian Schwarz <[email protected]> Closes #11602
1 parent b5fffa1 commit edc508a

File tree

2 files changed

+55
-19
lines changed

2 files changed

+55
-19
lines changed

include/sys/zfs_context.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ extern void random_fini(void);
652652

653653
struct spa;
654654
extern void show_pool_stats(struct spa *);
655-
extern int set_global_var(char *arg);
655+
extern int set_global_var(char const *arg);
656656

657657
typedef struct callb_cpr {
658658
kmutex_t *cc_lockp;

lib/libzpool/util.c

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,52 @@ show_pool_stats(spa_t *spa)
148148
nvlist_free(config);
149149
}
150150

151+
/* *k_out must be freed by the caller */
152+
static int
153+
set_global_var_parse_kv(const char *arg, char **k_out, u_longlong_t *v_out)
154+
{
155+
int err;
156+
VERIFY(arg);
157+
char *d = strdup(arg);
158+
159+
char *save = NULL;
160+
char *k = strtok_r(d, "=", &save);
161+
char *v_str = strtok_r(NULL, "=", &save);
162+
char *follow = strtok_r(NULL, "=", &save);
163+
if (k == NULL || v_str == NULL || follow != NULL) {
164+
err = EINVAL;
165+
goto err_free;
166+
}
167+
168+
u_longlong_t val = strtoull(v_str, NULL, 0);
169+
if (val > UINT32_MAX) {
170+
fprintf(stderr, "Value for global variable '%s' must "
171+
"be a 32-bit unsigned integer, got '%s'\n", k, v_str);
172+
err = EOVERFLOW;
173+
goto err_free;
174+
}
175+
176+
*k_out = k;
177+
*v_out = val;
178+
return (0);
179+
180+
err_free:
181+
free(k);
182+
183+
return (err);
184+
}
185+
151186
/*
152187
* Sets given global variable in libzpool to given unsigned 32-bit value.
153188
* arg: "<variable>=<value>"
154189
*/
155190
int
156-
set_global_var(char *arg)
191+
set_global_var(char const *arg)
157192
{
158193
void *zpoolhdl;
159-
char *varname = arg, *varval;
194+
char *varname;
160195
u_longlong_t val;
196+
int ret;
161197

162198
#ifndef _ZFS_LITTLE_ENDIAN
163199
/*
@@ -167,19 +203,12 @@ set_global_var(char *arg)
167203
*/
168204
fprintf(stderr, "Setting global variables is only supported on "
169205
"little-endian systems\n");
170-
return (ENOTSUP);
206+
ret = ENOTSUP;
207+
goto out_ret;
171208
#endif
172-
if (arg != NULL && (varval = strchr(arg, '=')) != NULL) {
173-
*varval = '\0';
174-
varval++;
175-
val = strtoull(varval, NULL, 0);
176-
if (val > UINT32_MAX) {
177-
fprintf(stderr, "Value for global variable '%s' must "
178-
"be a 32-bit unsigned integer\n", varname);
179-
return (EOVERFLOW);
180-
}
181-
} else {
182-
return (EINVAL);
209+
210+
if ((ret = set_global_var_parse_kv(arg, &varname, &val)) != 0) {
211+
goto out_ret;
183212
}
184213

185214
zpoolhdl = dlopen("libzpool.so", RTLD_LAZY);
@@ -189,18 +218,25 @@ set_global_var(char *arg)
189218
if (var == NULL) {
190219
fprintf(stderr, "Global variable '%s' does not exist "
191220
"in libzpool.so\n", varname);
192-
return (EINVAL);
221+
ret = EINVAL;
222+
goto out_dlclose;
193223
}
194224
*var = (uint32_t)val;
195225

196-
dlclose(zpoolhdl);
197226
} else {
198227
fprintf(stderr, "Failed to open libzpool.so to set global "
199228
"variable\n");
200-
return (EIO);
229+
ret = EIO;
230+
goto out_dlclose;
201231
}
202232

203-
return (0);
233+
ret = 0;
234+
235+
out_dlclose:
236+
dlclose(zpoolhdl);
237+
free(varname);
238+
out_ret:
239+
return (ret);
204240
}
205241

206242
static nvlist_t *

0 commit comments

Comments
 (0)