Repository navigation
Description
Summary
DecodeCipTcpIpInterfaceConfiguration (attribute 5) and DecodeCipTcpIpInterfaceHostName
(attribute 6) allocate a CIP string from the request and then return on the validation-failure
path without freeing it. The allocation is made into a function-local struct, so the pointer is
lost. Each rejected request leaks one allocation, and the requests are unauthenticated.
Verified against master 8c8fa9f.
Detail
source/src/cip/ciptcpipinterface.c:443 (attribute 5):
SetCipStringByData(&if_cfg.domain_name, domain_name_length,
message_router_request->data);
SetCipStringByData allocates via CipCalloc (cip/cipstring.c:179). Then at line 450:
if (!IsValidNetworkConfig(&if_cfg)
|| (domain_name_length > 0
&& !IsValidDomain(if_cfg.domain_name.string))) {
message_router_response->general_status = kCipErrorInvalidAttributeValue;
return number_of_decoded_bytes; /* if_cfg.domain_name.string is never freed */
}
if_cfg is a local, so on this return the allocation is unreachable.
The same shape is at ciptcpipinterface.c:486 and the return at :494 for the host name,
using the local tmp_host_name.
The only CipFree calls on these strings are at lines 687 and 692, in the shutdown path.
Reproduction
Build with OPENER_TCPIP_IFACE_CFG_SETTABLE=1 and AddressSanitizer, then over one session:
RegisterSessionSetAttributeSingleclass0xF5instance 1 attribute 3 = 0 (static IP; attribute 5 will not
decode otherwise)SetAttributeSingleclass0xF5instance 1 attribute 5, declaring a domain name length of 48
that failsIsValidDomain
Repeat step 3, then send SIGINT. LeakSanitizer after roughly 100 requests:
SUMMARY: AddressSanitizer: 4944 byte(s) leaked in 103 allocation(s)
4944 / 103 = 48 bytes, matching the declared length exactly: one lost allocation per request.
Note that RSS is not a useful instrument here, since 48 bytes per request is well below page
granularity.
Impact
Unauthenticated remote memory leak, up to 48 bytes per request on attribute 5 and 64 on
attribute 6, unbounded and repeatable. On the memory-constrained devices OpENer typically runs
on, sustained requests will exhaust the heap. It is resource exhaustion rather than memory
corruption.
This requires OPENER_TCPIP_IFACE_CFG_SETTABLE, which is 0 in the sample configurations but is
enabled for DLR devices and is described in opener_user_conf.h as required by ODVA
publication 70.
Suggested fix
Release the string on each failure return, using the existing helper:
ClearCipString(&if_cfg.domain_name); /* before the attribute 5 error return */
ClearCipString(&tmp_host_name); /* before the attribute 6 error return */
I have not sent a pull request, since #586 is open against these same functions and would
conflict; whoever lands that may prefer to fold this in.
Prior art checked
Searched on 2026-08-07 across open and closed issues and pull requests for ciptcpipinterface,
domain_name, SetCipStringByData, memory leak, TCP/IP object, CipCalloc:
- Fix: Memory leaks #601 ("Fix: Memory leaks"), merged: changes are in
ports/generic_networkhandler.cand free
socket error-message strings; does not touch this file. - Fix OOB read in TCP/IP interface hostname validation #586 ("Fix OOB read in TCP/IP interface hostname validation"), open: rewrites
IsValidNameLabelandIsValidDomainto take explicit lengths. It adds no frees, so this leak
remains after it is applied. - Remote stack-buffer-overflow read in default
SendRRData/SetAttributeSinglepath via overlong but decodable Padded EPath reaching real attribute decoders #569 and Malformed CPF and Get_Attribute_List request leads to pointer corruption anddenial of servicein OpENer #567 are separate out-of-bounds issues in other paths.
I may still have missed something. If this duplicates a report you already have, say so and I
will close it.
Activity