Audit report AR-11

Reported privately in an independent security review, 2026-08-07.

Report date
2026-08-07
Kind
Design
Status on master
Present
Proposed issue state
Open

Summary

When a client reads a struct, tuple, optional, vector or map result into an existing output object, libmultiprocess updates it one field or element at a time, so an exception partway through leaves the object partly updated. The report is accurate about the code. The exception still reaches the caller, so no partial value is ever presented as a successful result; this is the ordinary C++ basic exception guarantee. Design choice, not a bug.

The report

The struct, tuple, optional, vector and map codecs update or clear caller-owned output objects incrementally. If a later field or element throws while being read, the earlier fields stay modified. The report says this "can create state which no complete remote response ever represented". It lists partial compound-result failures among its suggested regression tests and gives no reproduction.

Proposed change

Deserialize into a temporary object and commit it to the caller's output only after every field has been read and validated. No code.

Assessment

True of the code on libmultiprocess master, where type-struct.h, type-tuple.h, type-optional.h, type-vector.h and type-map.h read into the destination in place through update().

When a read throws on the client side, clientInvoke (include/mp/proxy-types.h) captures the exception and rethrows it on the calling thread. The caller therefore always learns that the call failed. What it may see is an output or in/out argument left in a mixed state after the exception, which is the basic guarantee most C++ code gives. Whether in/out parameters should keep their old value on failure (the strong guarantee) is a design choice for the library, and it would cost a copy per call for large outputs.

Reaching a mid-read throw takes an allocation failure or a peer sending data its own serializer would not produce. No hostile peer scenario beyond that is involved.

Related: AR-13 is the same in-place pattern where the failure is worse: ReadDestUpdate::construct destroys the old value before building the new one, so a throw leads to a double destroy.

Status

Present on master by design. If the fix for AR-13 (build a temporary, then assign) is made, it could be extended to compound outputs at the same time. Idea; no issue open.

Reported privately in an independent security review on 2026-08-07.