#2391·aria2

Shift of a negative value in 'SocketCore::setIpDscp'

Author: 2rr0r4o3Created Aug 24, 2026Updated Aug 24, 2026

Summary

setIpDscp takes an int and shifts it left by two. --dscp is registered with a minimum of 0 and no maximum, so any value up to INT64_MAX is accepted; Option::getAsInt truncates it into an int that is usually negative by the time it arrives. Shifting a negative value left is undefined behaviour.

Details

/* src/SocketCore.h:142-148 */
142    // Set DSCP byte
143    void applyIpDscp();
144    static void setIpDscp(int ipDscp)
145    {
146      // Here we prepare DSCP value for IPTOS option, which sets whole DS field
147      ipDscp_ = ipDscp << 2;
148    }

The registration:

/* src/OptionHandlerFactory.cc:666 */
666      OptionHandler* op(new NumberOptionHandler(PREF_DSCP, TEXT_DSCP, "0", 0));

NumberOptionHandler(pref, desc, defaultValue, min, max, shortName) only min is given, so max keeps its default and no ceiling is enforced. Nothing between the option and the shift narrows the range to the six bits the field actually holds.

The call site is src/Context.cc:224:

224    SocketCore::setIpDscp(op->getAsInt(PREF_DSCP));

getAsInt has already truncated strtol's long into int32_t by this point, which is filed separately. --dscp=2147483647 arrives intact and overflows on the shift; --dscp=2147483648 and larger arrive already negative and the shift itself is UB.

Two changes are needed and either one alone silences this line. Give the option its real range at OptionHandlerFactory.cc:666:

cpp
    OptionHandler* op(new NumberOptionHandler(PREF_DSCP, TEXT_DSCP, "0", 0, 63));

and stop getAsInt from narrowing silently.

PoC

bash
autoreconf -i
mkdir build-asan && cd build-asan
CC=clang CXX=clang++ \
CPPFLAGS="-fsanitize=address,undefined -fsanitize=vptr -fno-omit-frame-pointer -g3 -O1" \
LDFLAGS="-fsanitize=address,undefined -fsanitize=vptr" \
../configure --disable-nls --with-openssl --without-gnutls --with-libxml2 \
             --with-sqlite3 --with-libz --without-libssh2 --without-libcares \
             --enable-bittorrent --enable-metalink
make -j"$(nproc)" -C deps && make -j"$(nproc)" -C src
bash
for V in 2147483647 2147483648 9223372036854775807; do
  UBSAN_OPTIONS=print_stacktrace=1 ./src/aria2c --dscp="$V" --dry-run=true \
    --max-tries=1 --dir=/tmp/poc --no-conf=true http://127.0.0.1:1/x
done
=== --dscp=2147483647
src/SocketCore.h:147:22: runtime error: left shift of 2147483647 by 2 places
    cannot be represented in type 'int'

=== --dscp=2147483648
src/SocketCore.h:147:22: runtime error: left shift of negative value -2147483648

=== --dscp=9223372036854775807
src/SocketCore.h:147:22: runtime error: left shift of negative value -1
    #0 aria2::SocketCore::setIpDscp(int)   src/SocketCore.h:147:22
    #1 aria2::Context::Context(...)        src/Context.cc:224
    [...]
SUMMARY: UndefinedBehaviorSanitizer: invalid-shift-base src/SocketCore.h:147:22

Impact

Undefined behaviour reachable from local configuration or from an RPC client that can call changeGlobalOption. The resulting ipDscp_ is applied with setsockopt(IP_TOS), so effect is a garbage DS field rather than memory corruption.