Compare commits

..

5 Commits

Author SHA1 Message Date
Evgeny Vereshchagin 50f5e2e281
Merge pull request #15309 from poettering/strv-split
remove dead-lock in strv_split()
2020-04-03 06:37:54 +03:00
Lennart Poettering 82aa14e2a3 string-util: make clear that split() + FOREACH_WORD() should die 2020-04-02 16:43:39 +02:00
Lennart Poettering 29965a2a6e string-util: make sure we eat even half complete words in split()
split() and FOREACH_WORD really should die, and everything be moved to
extract_first_word() and friends, but let's at least make sure that for
the remaining code using it we can't deadlock by not progressing in the
word iteration.

Fixes: #15305
2020-04-02 16:43:36 +02:00
Lennart Poettering 5fed82c642 string-util: some minor coding style updates 2020-04-02 16:43:24 +02:00
Lennart Poettering 77b19caf6b update TODO 2020-04-02 16:42:20 +02:00
4 changed files with 52 additions and 30 deletions

4
TODO
View File

@ -117,7 +117,8 @@ Features:
* systemd-repart: allow config of partition uuid * systemd-repart: allow config of partition uuid
* userdb: allow username prefix searches in varlink API * userdb: allow username prefix searches in varlink API, allow realname and
realname substr searches in varlink API
* userdb: allow existence checks * userdb: allow existence checks
@ -864,6 +865,7 @@ Features:
make assumptions about their slice anymore. make assumptions about their slice anymore.
- follow PropertiesChanged state more closely, to deal with quick logouts and - follow PropertiesChanged state more closely, to deal with quick logouts and
relogins relogins
- (optionally?) spawn seat-manager@$SEAT.service whenever a seat shows up that as CanGraphical set
* journal: * journal:
- consider introducing implicit _TTY= + _PPID= + _EUID= + _EGID= + _FSUID= + _FSGID= fields - consider introducing implicit _TTY= + _PPID= + _EUID= + _EGID= + _FSUID= + _FSGID= fields

View File

@ -113,7 +113,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
bool escaped = false; bool escaped = false;
int n; int n;
for (n=0; s[n]; n++) { for (n = 0; s[n] != '\0'; n++) {
if (escaped) if (escaped)
escaped = false; escaped = false;
else if (s[n] == '\\') else if (s[n] == '\\')
@ -122,50 +122,62 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
break; break;
} }
/* if s ends in \, return index of previous char */ return n;
return n - escaped;
} }
/* Split a string into words. */ /* Split a string into words. */
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) { const char* split(
const char **state,
size_t *l,
const char *separator,
SplitFlags flags) {
const char *current; const char *current;
assert(state);
assert(l);
if (!separator)
separator = WHITESPACE;
current = *state; current = *state;
if (!*current) { if (*current == '\0') /* already at the end? */
assert(**state == '\0');
return NULL; return NULL;
}
current += strspn(current, separator); current += strspn(current, separator); /* skip leading separators */
if (!*current) { if (*current == '\0') { /* at the end now? */
*state = current; *state = current;
return NULL; return NULL;
} }
if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) { if (FLAGS_SET(flags, SPLIT_QUOTES)) {
char quotechars[2] = {*current, '\0'};
*l = strcspn_escaped(current + 1, quotechars); if (strchr(QUOTES, *current)) {
if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || /* We are looking at a quote */
(current[*l + 2] && !strchr(separator, current[*l + 2]))) { *l = strcspn_escaped(current + 1, CHAR_TO_STR(*current));
/* right quote missing or garbage at the end */ if (current[*l + 1] != *current ||
if (flags & SPLIT_RELAX) { (current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) {
*state = current + *l + 1 + (current[*l + 1] != '\0'); /* right quote missing or garbage at the end */
return current + 1; if (FLAGS_SET(flags, SPLIT_RELAX)) {
*state = current + *l + 1 + (current[*l + 1] != '\0');
return current + 1;
}
*state = current;
return NULL;
} }
*state = current; *state = current++ + *l + 2;
return NULL;
} else {
/* We are looking at a something that is not a quote */
*l = strcspn_escaped(current, separator);
if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) {
/* unfinished escape */
*state = current;
return NULL;
}
*state = current + *l;
} }
*state = current++ + *l + 2;
} else if (flags & SPLIT_QUOTES) {
*l = strcspn_escaped(current, separator);
if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) {
/* unfinished escape */
*state = current;
return NULL;
}
*state = current + *l;
} else { } else {
*l = strcspn(current, separator); *l = strcspn(current, separator);
*state = current + *l; *state = current + *l;

View File

@ -112,8 +112,10 @@ typedef enum SplitFlags {
SPLIT_RELAX = 0x01 << 1, SPLIT_RELAX = 0x01 << 1,
} SplitFlags; } SplitFlags;
/* Smelly. Do not use this anymore. Use extract_first_word() instead! */
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
/* Similar, don't use this anymore */
#define FOREACH_WORD(word, length, s, state) \ #define FOREACH_WORD(word, length, s, state) \
_FOREACH_WORD(word, length, s, WHITESPACE, 0, state) _FOREACH_WORD(word, length, s, WHITESPACE, 0, state)

View File

@ -307,6 +307,12 @@ static void test_strv_split(void) {
l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX); l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l); assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted)); assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_split_full("\\", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, STRV_MAKE("\\")));
} }
static void test_strv_split_empty(void) { static void test_strv_split_empty(void) {