Signed 'char' passed to an 'unsigned char' parameter in 'GroupId::toNumericId'
Summary
The same defect as in GroupId::expandUnique, in the function that parses a full 16-digit GID. hex[i] is char; util::hexCharToUInt takes unsigned char; bytes at or above 0x80 change value at the call. The entry point here is --gid, which also arrives from a saved session file, so fixing expandUnique alone leaves this one open.
Details
/* src/GroupId.cc:106-120 */
106 int GroupId::toNumericId(a2_gid_t& n, const char* hex)
107 {
108 a2_gid_t p = 0;
109 size_t i;
110 for (i = 0; hex[i]; ++i) {
111 unsigned int c = util::hexCharToUInt(hex[i]);
112 if (c == 255) {
113 return ERR_INVALID;
114 }
115 p <<= 4;
116 p |= c;
117 }/* src/util.h:249 */
249 unsigned int hexCharToUInt(unsigned char ch);The reject at :112 does its job — a non-hex byte returns 255 and the function bails. so the converted value never reaches p. The conversion at :111 still happens first.
Reached through getGID in download_helper.cc:112, which is on the path for every request group created from the command line or from --input-file:
aria2::GroupId::toNumericId(...) src/GroupId.cc:111
aria2::(anonymous namespace)::getGID(...) src/download_helper.cc:112
aria2::(anonymous namespace)::createRequestGroup(...) src/download_helper.cc
aria2::createRequestGroupForUri(...)A fix belongs at :111, the same as for expandUnique:
unsigned int c = util::hexCharToUInt(static_cast<unsigned char>(hex[i]));An unsigned char overload of hexCharToUInt taking char would close both at once.
PoC
Requires the implicit-conversion checks, which -fsanitize=undefined does not include. Build as in the bittorrent::processRootDictionarycase (-fsanitize=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change plus the ignorelist that silences bitfield.h, base64.h, the util.h byte helpers and deps/).
GID=$(python3 -c 'import sys; sys.stdout.write(bytes([0xff,0xfe]+list(range(0x80,0x8e))).decode("latin-1"))')
UBSAN_OPTIONS=print_stacktrace=1 ./src/aria2c --gid="$GID" --dry-run=true \
--max-tries=1 --dir=/tmp/poc --no-conf=true http://127.0.0.1:1/xsrc/GroupId.cc:111:42: runtime error: implicit conversion from type 'char' of value -61
(8-bit, signed) to type 'unsigned char' changed the value to 195 (8-bit, unsigned)
#0 aria2::GroupId::toNumericId(unsigned long&, char const*) src/GroupId.cc:111:42
#1 aria2::(anonymous namespace)::getGID(...) src/download_helper.cc:112:9
#2 aria2::(anonymous namespace)::createRequestGroup(...) src/download_helper.cc
#3 aria2::createRequestGroupForUri(...)
[...]
SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change src/GroupId.cc:111:42Impact
Implementation-defined conversion at a call boundary, reachable from the command line and from a session file. Just Type correctness issue.
Source: aria2/aria2