This variant analysis looks for calls to signal-unsafe functions by signal handlers. Note that the scope of this analysis is limited to identifying the potential of apparently reachable unsafe paths, not verifying whether reachibility conditions, nor verifying whether or not those paths are exploitable.
This analysis starts with showing how to checkout and index the relevant version of the code, then how to discover the unsafe paths through the call graph manually using example tools, and finally how to use the Python API to script up a simple but generic checker for this kind of issue.
git clone https://github.com/openssh/openssh-portable.git
cd openssh-portableThen, checkout OpenSSH [9.7p1](https://github.com/openssh/openssh-portable/releases/tag/V_9_7_P1:
git checkout 86bdd3853f4d32c85e295e6216a2fe0953ad93f0autoreconf
CC=`which clang` CXX=`which clang++` ./configure --with-ssl-dir=$HOME/.brew/opt/opensslNote: You may need to fiddle with the configure invocation. For example, I needed to specify --with-ssl-dir on my machine to avoid a libcrypto issue.
mkdir /tmp/clang_compile_commands
CCC_OVERRIDE_OPTIONS="# +-gen-cdb-fragment-path +/tmp/clang_compile_commands " \
make allAfter this, we should see the following:
% ls /tmp/clang_compile_commands
addr.c.5e13.json fnmatch.c.730f.json session.c.caa5.json
addrmatch.c.aa04.json freezero.c.2da8.json setenv.c.4b9f.json
arc4random.c.ef99.json getcwd.c.cb71.json setproctitle.c.9167.jsonUsing the combine_compile_commands.py to create a single compilation database for indexing.
% python3 /path/to/multiplier-checkout/scripts/combine_compile_commands.py /tmp/clang_compile_commands/*.json >compile_commands.jsonNext, we'll run mx-index to index OpenSSH.
% time ./bin/mx-index --db /tmp/openssh.db --workspace /tmp/openssh.ws --target compile_commands.json --generate_sourceir --show_progress
Commands (237 / 237) 100% [||||||||||||||||||||||||||||||||||||||||]
Evaluated commands (237 / 237) 100% [||||||||||||||||||||||||||||||||||||||||]
Parsing (237 / 237) 100% [||||||||||||||||||||||||||||||||||||||||]
File serialization (663 / 663) 100% [||||||||||||||||||||||||||||||||||||||||]
AST partitioning (237 / 237) 100% [||||||||||||||||||||||||||||||||||||||||]
Fragment serialization (30601 / 30601) 100% [||||||||||||||||||||||||||||||||||||||||]
Type serialization (18416 / 18416) 100% [||||||||||||||||||||||||||||||||||||||||]
mx-index --db /tmp/openssh.db --workspace /tmp/openssh.ws --target 246.65s user 48.49s system 523% cpu 56.417 totalHere we told mx-index to save its database to /tmp/openssh.db (we'll need this soon), and its temporary workspace to the directory /tmp/openssh.ws. We can now delete /tmp/openssh.ws, as its only needed if we wanted to index additional projects into the same database.
rm -rf /tmp/openssh.wsThe description of regreSSHion states:
The
SIGALRMhandler of this OpenSSH version callspacket_close(), which callsbuffer_free(), which callsxfree()and hencefree(), which is not async-signal-safe.
We'll start by checking this with the test tools provided in the SDK. This is not the actual way I would recommend doing anything in practice, as these tools are designed as examples of how to use the API, as well as functionality tests of the API -- they are not designed specifically for productivity or composition. Later we'll use scripting to turn this into automated checker.
We'll start by trying to understand the specific SIGALRM signal. First, lets locate the entity:
% mx-find-symbol --db /tmp/openssh.db --name SIGALRM --exact
1152921504606847020 2305843009214760458 13314892317121839104 SIGALRM DEFINE_DIRECTIVEThe third id, 13314892317121839104, is the entity ID of SIGALRM. Next, lets go and find all uses of SIGALRM:
% mx-highlight-references --db /tmp/openssh.db --entity_id 13314892317121839104The first reference looks like this:
So this says there's a function, ssh_signal, taking in a signal number signum and a signal handler handler, and it attaches handler to the signum using sigaction. In some other uses, we see a handler sig_alarm and grace_alarm_handler registered for SIGALRM via ssh_signal:
So next we can look for paths between sig_alarm or grace_alarm_handler and a async signal unsafe function, such as free.
Next, we'll find free:
% mx-find-symbol --db /tmp/openssh.db --name free --exact
1152921504606847195 2305843009214751135 9475573625012617216 free FUNCTIONThe third number, 9475573625012617216, is the entity ID of free. Now lets
look for sig_alarm and grace_alarm_handler:
% mx-find-symbol --db /tmp/openssh.db --name sig_alarm --exact
1152921504606847411 2305843009214762518 9281918852971626496 sig_alarm FUNCTION
1152921504606847411 2305843009214762498 9475573636927586304 sig_alarm FUNCTIONThere are two of them, so one is probably a definition and the other is probably a declaration. We can choose the entity ID of either of them.
% mx-find-symbol --db /tmp/openssh.db --name grace_alarm_handler --exact
1152921504606847515 2305843009214768730 9281918859485380608 grace_alarm_handler FUNCTIONNext, lets see if we can find a path from sig_alarm or grace_alarm_handler to free:
% mx-print-call-graph --db /tmp/openssh.db --entity_id 9475573625012617216 --reachable_from_entity_id 9281918852971626496 >/tmp/sig_alarm_to_free.dot% mx-print-call-graph --db /tmp/openssh.db --entity_id 9475573625012617216 --reachable_from_entity_id 9281918859485380608 >/tmp/grace_alarm_handler_to_free.dotThis creates the call graphs of free rooted at sig_alarm and grace_alarm_handler, respectively. The output of the mx-print-call-graph is a DOT digraph. There are no edges in the sig_alarm to free graph, so we'll focus on the grace_alarm_handler to free graph:
% xdot /tmp/grace_alarm_handler_to_free.dotWith output looking like this:
We can see that from grace_alarm_handler, we can reach sshfata via xmalloc or get_sock_port, and from there cleanup_exit provides paths to free.
We have now manually confirmed the rough reachability details of the CVE, i.e. that an async-signal unsafe function can potentially be invoked by a signal handler in OpenSSH.



