diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 0ff8d41c1e6ce..797ae011b7420 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -525,14 +525,21 @@ static void php_snmp_internal(INTERNAL_FUNCTION_PARAMETERS, int st, } else if (st & SNMP_USE_SUFFIX_AS_KEYS && st & SNMP_CMD_WALK) { snprint_objid(buf2, sizeof(buf2), vars->name, vars->name_length); if (rootlen <= vars->name_length && snmp_oid_compare(root, rootlen, vars->name, rootlen) == 0) { - buf2[0] = '\0'; + size_t pos = 0; count = rootlen; - while(count < vars->name_length){ - snprintf(buf, sizeof(buf), "%lu.", vars->name[count]); - strcat(buf2, buf); + while (count < vars->name_length) { + int written = snprintf(buf2 + pos, sizeof(buf2) - pos, "%lu.", vars->name[count]); + if (written < 0 || (size_t)written >= sizeof(buf2) - pos) { + break; + } + pos += (size_t)written; count++; } - buf2[strlen(buf2) - 1] = '\0'; /* remove trailing '.' */ + if (pos > 0) { + buf2[pos - 1] = '\0'; /* remove trailing '.' */ + } else { + buf2[0] = '\0'; + } } add_assoc_zval(return_value, buf2, &snmpval); } else { diff --git a/ext/snmp/tests/gh21341.phpt b/ext/snmp/tests/gh21341.phpt new file mode 100644 index 0000000000000..291c83e0633c6 --- /dev/null +++ b/ext/snmp/tests/gh21341.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-21341: suffix-as-keys walk uses offset-tracked snprintf (no strcat overflow) +--EXTENSIONS-- +snmp +--SKIPIF-- + +--FILE-- +walk('.1.3.6.1.2.1', /* suffix_as_keys */ true); + +var_dump(is_array($result)); +var_dump(count($result) > 0); + +/* Every key must be a bare dotted-numeric suffix, not a full OID. + * Full OIDs start with '.'; suffixes do not. */ +$bad = array_filter(array_keys($result), static function (string $k): bool { + return $k[0] === '.' || !preg_match('/^\d[\d.]*$/', $k); +}); +var_dump(count($bad) === 0); + +/* No key may end with '.': the trailing-dot removal (buf2[pos-1] = '\0') + * must fire correctly on every entry. */ +$trailing = array_filter(array_keys($result), static function (string $k): bool { + return $k !== '' && $k[-1] === '.'; +}); +var_dump(count($trailing) === 0); + +var_dump($session->close()); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true)