diff options
| author | Holger Weiss <holger@zedat.fu-berlin.de> | 2026-06-30 15:58:09 +0200 |
|---|---|---|
| committer | Holger Weiss <holger@zedat.fu-berlin.de> | 2026-06-30 15:58:09 +0200 |
| commit | a675995b19a6315f1b033a7c1ca980b5fbdc408d (patch) | |
| tree | 6a0dad9da2821f841fa624578cfe98ae1d309ffb | |
| parent | c35c12e58d326ffbd6cfb3c9097653f9f3fb2f4a (diff) | |
| download | monitoring-plugins-a675995b19a6315f1b033a7c1ca980b5fbdc408d.tar.gz | |
check_icmp: Fix parsing of single-char threshold
The threshold parser starts a pointer at the last character of the
string and walks it backwards until it reaches the second character.
This assumes the string is at least two characters long. For a
single-character threshold such as "-w 1" or "-c 1", the pointer
underflows past the start of the string and keeps dereferencing memory
out of bounds.
Beyond the out-of-bounds reads, an out-of-bounds write can occur if a
stray '%' or ',' byte happens to turn up while scanning backwards. In
that case, the parser writes a NUL byte at that out-of-bounds address
(the ',' case additionally re-reads forward from there via strtoul(3)).
Only run the descending scan when the string has at least two
characters, leaving the behaviour for all valid thresholds unchanged.
Reported-by: Christopher Kreft <Email@ChristopherKreft.de>
| -rw-r--r-- | plugins-root/check_icmp.c | 25 |
1 files changed, 14 insertions, 11 deletions
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index f3c83ee0..8f5c1fe4 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c | |||
| @@ -1926,18 +1926,21 @@ static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold thres | |||
| 1926 | } | 1926 | } |
| 1927 | 1927 | ||
| 1928 | /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ | 1928 | /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ |
| 1929 | bool is_at_last_char = false; | 1929 | size_t len = strlen(str); |
| 1930 | char *tmp = &str[strlen(str) - 1]; | 1930 | if (len >= 2) { |
| 1931 | while (tmp != &str[1]) { | 1931 | bool is_at_last_char = false; |
| 1932 | if (*tmp == '%') { | 1932 | char *tmp = &str[len - 1]; |
| 1933 | *tmp = '\0'; | 1933 | while (tmp != &str[1]) { |
| 1934 | } else if (*tmp == ',' && is_at_last_char) { | 1934 | if (*tmp == '%') { |
| 1935 | *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */ | 1935 | *tmp = '\0'; |
| 1936 | result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0); | 1936 | } else if (*tmp == ',' && is_at_last_char) { |
| 1937 | break; | 1937 | *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */ |
| 1938 | result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0); | ||
| 1939 | break; | ||
| 1940 | } | ||
| 1941 | is_at_last_char = true; | ||
| 1942 | tmp--; | ||
| 1938 | } | 1943 | } |
| 1939 | is_at_last_char = true; | ||
| 1940 | tmp--; | ||
| 1941 | } | 1944 | } |
| 1942 | 1945 | ||
| 1943 | get_timevar_wrapper parsed_time = get_timevar(str); | 1946 | get_timevar_wrapper parsed_time = get_timevar(str); |
