. "$BST_LIB" # BST-TIMEOUT: 300 # one probe run plus ~60 shell extractions. The probe's # own work is bounded and syscall-free apart from four # calls -- one gethostname, one getaddrinfo, one # gethostbyname and one failing connect(2) -- so the # bound is headroom for a # loaded host, not a cap on anything this suite is known # to approach. # libSM 1.2.4 -- the X Session Management library (poky recipes-graphics/xorg-lib). # See libsm_%.bbappend for what the port adds and why. # # --------------------------------------------------------------------------- # THERE IS NO SESSION MANAGER ON THIS IMAGE AND THERE WILL NOT BE ONE # # Everything libSM exists to do is a conversation with a session manager over # ICE: register a client, be told to save state, be told to die. None of that is # reachable here. libsm is in this epic for one reason -- libXt, the next member # and the base of every Xt toolkit, carries it in DEPENDS and links it # unconditionally -- so what this suite has to answer is not "does session # management work" but "is this library CORRECT ON THIS ARM for the day libXt # binds to it". # # That question has real content, and none of it needs a peer: # # THE ABI SmProp and SmPropValue are filled in BY THE CALLER. libXt # writes one on its own stack and hands the address across, so # the layout is a contract between two separately compiled # objects. On wasm32 a pointer is 4 bytes; on the build host 8. # THE WIRE SMproto.h declares each message struct AND the size it must # have (sz_smSaveYourselfMsg = 16 and so on), under a comment # reading "These better be multiples of 8". The header carries # its own reference, so unlike 57-xorgproto.test this suite needs # NO build-host comparison -- the probe checks sizeof against the # sz_ constant beside it and reports whether they agree. # THE FREES SmFreeProperty and SmFreeReasons are the only two pieces of the # property path a client calls directly. They unwind five # allocations per property, so a wrong struct offset here is a # FAULT rather than a wrong answer. # THE REFUSALS SmcOpenConnection has two documented failures reachable with # nothing listening, and they must come back DISTINGUISHABLE. # # --------------------------------------------------------------------------- # ONE RED IS EXPECTED ON THIS IMAGE AND IT IS THE IMAGE'S, NOT libSM's # # SmsGenerateClientID builds a client id out of THIS MACHINE'S OWN IP ADDRESS, # which means it runs a name lookup on the hostname. On this image that lookup # cannot answer: the hostname is brintos-wasm32 (base-files), /etc/hosts carries # 127.0.0.1 only, nsswitch says `hosts: files dns' with no nameserver behind it, # and an NSS module cannot be loaded on this arm at all because the glibc # overlay makes __libc_dlopen_mode return NULL by design # (brintos/linux-in-the-browser#92, closed; re-measured under BRINTOS-303). # # sm_genid.c's DOCUMENTED answer when that lookup returns NULL is to return # NULL. So a NULL client id here is the library behaving correctly, and section # 5 scores it as a pass -- while asserting the id's full documented shape on an # image where the name DOES resolve, so the case stops being vacuous the day one # does. Stock poky behaves identically on any board with no hosts entry for its # own name; there is no brintOS defect here and nothing to work around. # # --------------------------------------------------------------------------- # READ A RED IN SECTION ORDER; THE FIRST SECTION TO GO RED LOCALISES THE DEFECT # # 1 the files arrived packaging only # 2 sm.pc resolves consumers find the library at all # 3 the probe runs to its end the linked code executes here # 4 the ABI and the wire struct layout on this arm # 5 the constants client and manager agree on names # 6 SmsGenerateClientID the one function that reads the image # 7 the property encoding round trip the arithmetic libXt will drive # 8 SmFreeProperty / SmFreeReasons the two frees a client calls # 9 the two documented refusals SmcOpenConnection with no peer # # A build where 3 passes and 4 does not has a struct-layout fault and nothing # below it can be trusted. 7 failing while 4 passes is arithmetic, not layout. # 9 failing on its own is libICE's or xtrans's, not libSM's -- case # libsm/open-dead-socket is the one that reaches them. # --------------------------------------------------------------------------- # A root prefix, empty on the target and set only when this suite is validated # NATIVELY under dash before it ships. The runner never sets it, so on the image # every path below is exactly the absolute one. R="${BST_LIBSM_ROOT:-}" INC=$R/usr/include/X11/SM PCD=$R/usr/lib/pkgconfig LIB=$R/usr/lib SMT=$R/usr/libexec/libsm-tests work=${TMPDIR:-/tmp}/bst-libsm.$$ rm -rf "$work"; mkdir -p "$work" hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "missing or empty: $2"; fi } # sm_line # # The prefix is matched with grep -F rather than the whole line, so a red # reports WHAT THE PROBE ACTUALLY SAID for that case instead of merely # "absent". Every prefix is unique by construction: each label below is # followed by a separator that ends it. sm_line() { _l=$(grep -F -- "$2" "$work/out" 2>/dev/null | head -1) check_eq "$1" "$2$3" "$_l" } # --------------------------------------------------------------------------- # 0. THE CANARY, which is not an assertion about libSM at all. # # The guest console drops a suite's FIRST BST result line before the host parser # sees it -- brintos/linux#96. The harness computes its pass count as # total-minus-failures, so THE DROPPED LINE IS COUNTED AS A PASS, and a suite # whose first case is a genuine `not ok' loses the one line that mattered. So # the first line out of this file asserts something already true. # Remove this case when #96 is fixed. bst_ok libsm/suite-canary # --------------------------------------------------------------------------- # 1. THE PAYLOAD. # # Three headers, each by name: libXt and every session-aware client #includes # one of them by name, so a missing one fails somebody else's do_compile rather # than anything on this image. check_rc libsm/dir-SM 0 test -d "$INC" hasfile libsm/hdr-SM-h "$INC/SM.h" hasfile libsm/hdr-SMlib-h "$INC/SMlib.h" hasfile libsm/hdr-SMproto-h "$INC/SMproto.h" # THE ARCHIVE, AND THE ABSENCE OF A SHARED OBJECT. This distro appends # --disable-shared to every class-target configure, so libtool produces # libSM.a and no libSM.so -- which is why ${PN} is empty, why the image names # libsm-staticdev rather than a bare libsm, and why the probe is statically # linked. A libSM.so appearing here would mean the distro-wide flag stopped # reaching this recipe, and every consumer's link line would change silently. hasfile libsm/archive "$LIB/libSM.a" check_rc libsm/no-shared-object 1 test -e "$LIB/libSM.so" hasfile libsm/probe-shipped "$SMT/libsm-probe" # SMlib.h pulls ICElib.h in on its first page, so a consumer that has libSM's # headers and not libICE's cannot compile a single line. Asserted here because # it is a cross-package fact that no libSM file records. check_rc libsm/icelib-reachable 0 test -f "$R/usr/include/X11/ICE/ICElib.h" # --------------------------------------------------------------------------- # 2. sm.pc -- how a consumer finds any of the above. # # Unlike xtrans's, this .pc is architecture-DEPENDENT (it carries a libdir and # a -lSM), so ${libdir}/pkgconfig is the right home for it and share would be # the defect. The two packages are pinned in opposite directions on purpose. hasfile libsm/pc-in-libdir "$PCD/sm.pc" # NOT `require pkg-config'. require ENDS THE FILE when the tool is absent, and # everything this suite exists for -- sections 3 to 9 -- comes after this one. if command -v pkg-config >/dev/null 2>&1; then bst_ok libsm/pkg-config-present check libsm/pc-exists pkg-config --exists sm check libsm/pc-validate pkg-config --validate sm check_eq libsm/pc-modversion 1.2.4 "$(pkg-config --modversion sm 2>/dev/null)" # ice AND xproto are on Requires.PRIVATE, not Requires -- upstream puts them # there because a consumer of the SHARED library does not need to name them, # and this distro has no shared library at all. So on THIS image the private # line is the load-bearing one: libSM's own objects call into libICE, and a # consumer static-linking -lSM without -lICE gets undefined symbols nobody # would attribute to this package. Asserted with --static, which is what # every consumer here is. check libsm/pc-static-libs-name-ICE sh -c \ "PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --static --libs sm | grep -q -- -lICE" # PKG_CONFIG_ALLOW_SYSTEM_LIBS is required for the same reason # 57-xtrans.test needs its CFLAGS counterpart: pkg-config SUPPRESSES a -L # naming a directory already on the linker's system path, so the bare query # answers correctly and uselessly. The variable asks for the flag pkg-config # COMPUTED rather than the one it decided to print. libs=$(PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs sm 2>/dev/null) case "$libs" in *-lSM*) bst_ok libsm/pc-libs-names-SM ;; *) bst_notok libsm/pc-libs-names-SM "no -lSM in '$(_bst_1line "$libs")'" ;; esac else bst_skip libsm/pkg-config-present "pkg-config not installed on this image" fi # --------------------------------------------------------------------------- # 3. THE PROBE RUNS, AND RUNS TO ITS END. # # do_install ships it 0755, but the rootfs is read-only at browser runtime and # the exec bit is not reliably readable there either, so it is copied to /tmp -- # a mode=1777 tmpfs with no noexec -- and given the bit where the copy lands. probe=$work/libsm-probe if cp "$SMT/libsm-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then "$probe" >"$work/out" 2>"$work/err" rc=$? # The probe exits 0 whatever it measures -- every expectation lives in this # file, not in the binary -- so a non-zero status here means it did not RUN: # a trap, a missing import, an exec refusal. On this arm that is a real and # common failure and is what this case is for. if [ "$rc" -eq 0 ]; then bst_ok libsm/probe-runs else bst_notok libsm/probe-runs "rc=$rc $(_bst_1line "$(cat "$work/err")")"; fi total=$(sed -n 's/^SM-TOTAL //p' "$work/out") if [ -n "$total" ]; then bst_ok libsm/probe-complete else bst_notok libsm/probe-complete \ "no SM-TOTAL line: $(_bst_1line "$(cat "$work/out")")"; fi # The measurement count, bounded FROM BELOW. Adding a fixture raises it; a # probe that got THINNER while still terminating cleanly is what this # catches, and nothing else here would. if [ -n "$total" ] && [ "$total" -ge 40 ]; then bst_ok libsm/probe-coverage else bst_notok libsm/probe-coverage \ "reported ${total:-0} measurements, expected not less than 40"; fi # ------------------------------------------------------------------------- # 4. THE ABI AND THE WIRE. # THE DATA MODEL, and this is the one line where agreeing with the build host # would be the defect: `long' and a pointer are 4 bytes on wasm32 and 8 on # x86-64. sm_line libsm/data-model "SM MODEL " "long=4 ptr=4 int=4" # The two caller-filled structures, derived from that model and stated # absolutely: SmPropValue is {int length; void *value} and SmProp is # {char *name; char *type; int num_vals; SmPropValue *vals}, all four fields # 4 bytes here with no padding anywhere. SmcCallbacks is four {proc, pointer} # pairs and SmsCallbacks is ten of them. sm_line libsm/abi-sizes "SM SIZE " \ "SmPropValue=8 SmProp=16 SmcCallbacks=32 SmsCallbacks=80" # The offsets, separately from the sizes. A struct can be the right total # size with its members in the wrong places -- and a caller writing # prop.num_vals into what the library reads as prop.type is a crash inside # SmFreeProperty, not a wrong answer. sm_line libsm/abi-offsets-value "SM OFF SmPropValue " "length=0 value=4" sm_line libsm/abi-offsets-prop "SM OFF SmProp " \ "name=0 type=4 num_vals=8 vals=12" # THE WIRE, EACH MESSAGE AGAINST THE SIZE ITS OWN HEADER DECLARES. The # sizeof and the sz_ constant are asserted to AGREE rather than to equal a # number written here: the number is upstream's and moves with an upstream # bump, whereas the agreement is what must never stop holding. Every one of # these is a byte count some other machine reads off the wire, so a # disagreement is not a local matter. sm_line libsm/wire-registerclient "SM WIRE smRegisterClientMsg = " \ "sizeof=8 sz=8 agree=yes" sm_line libsm/wire-registerclientreply "SM WIRE smRegisterClientReplyMsg = " \ "sizeof=8 sz=8 agree=yes" # The two 16-byte messages, and the only two that carry payload fields in # their fixed part. They are where a padding change would actually land: # four CARD8s after a CARD32, then explicit unused bytes to the next # multiple of 8. An arm that padded after the CARD32 would give 20 or 24. sm_line libsm/wire-saveyourself "SM WIRE smSaveYourselfMsg = " \ "sizeof=16 sz=16 agree=yes" sm_line libsm/wire-saveyourselfrequest "SM WIRE smSaveYourselfRequestMsg = " \ "sizeof=16 sz=16 agree=yes" sm_line libsm/wire-interactrequest "SM WIRE smInteractRequestMsg = " \ "sizeof=8 sz=8 agree=yes" sm_line libsm/wire-interactdone "SM WIRE smInteractDoneMsg = " \ "sizeof=8 sz=8 agree=yes" sm_line libsm/wire-saveyourselfdone "SM WIRE smSaveYourselfDoneMsg = " \ "sizeof=8 sz=8 agree=yes" sm_line libsm/wire-die "SM WIRE smDieMsg = " "sizeof=8 sz=8 agree=yes" sm_line libsm/wire-closeconnection "SM WIRE smCloseConnectionMsg = " \ "sizeof=8 sz=8 agree=yes" sm_line libsm/wire-setproperties "SM WIRE smSetPropertiesMsg = " \ "sizeof=8 sz=8 agree=yes" sm_line libsm/wire-getproperties "SM WIRE smGetPropertiesMsg = " \ "sizeof=8 sz=8 agree=yes" # And the census: every message the probe measured agreed. Without this a # message added upstream could disagree and no case above would name it. disagree=$(grep -c 'agree=no' "$work/out" 2>/dev/null) check_eq libsm/wire-all-agree 0 "${disagree:-x}" # ------------------------------------------------------------------------- # 5. THE CONSTANTS a client and a manager must agree on before either speaks. # # Read out of the INSTALLED headers by the probe, so this section is what # catches an install that shipped one version's SM.h beside another # version's library -- which compiles, links and then puts the wrong opcode # on the wire. sm_line libsm/proto-version "SM PROTO " "major=1 minor=0" sm_line libsm/savetype "SM SAVETYPE " "global=0 local=1 both=2" sm_line libsm/restart-style "SM RESTART " \ "ifrunning=0 anyway=1 immediately=2 never=3" sm_line libsm/opcodes "SM OPCODE " \ "error=0 registerclient=1 registerclientreply=2 saveyourself=3 die=9 closeconnection=11 setproperties=12 getproperties=14 propertiesreply=15 savecomplete=18" # The property names and types are STRINGS on the wire, not enumerations, so # a typo in a header is invisible to every compiler and fatal to # interoperability. Quoted, so a trailing space would be visible. sm_line libsm/name-program "SM NAME Program=" "'Program'" sm_line libsm/name-restart "SM NAME RestartCommand=" "'RestartCommand'" sm_line libsm/name-clone "SM NAME CloneCommand=" "'CloneCommand'" sm_line libsm/name-discard "SM NAME DiscardCommand=" "'DiscardCommand'" sm_line libsm/name-stylehint "SM NAME RestartStyleHint=" "'RestartStyleHint'" sm_line libsm/types "SM TYPE " \ "CARD8='CARD8' ARRAY8='ARRAY8' LISTofARRAY8='LISTofARRAY8'" # ------------------------------------------------------------------------- # 6. SmsGenerateClientID -- the one function here that measures the IMAGE. # # See the header of this file. The lookup is reported by the probe BEFORE # the ids are, so the two cases below can be scored against each other # rather than against an assumption about this image. check_eq libsm/hostname brintos-wasm32 \ "$(sed -n 's/^SM HOSTNAME //p' "$work/out")" # BOTH lookups, because which one libSM makes is decided at ITS configure # time: with IPv6 compiled in sm_genid.c calls getaddrinfo and without it # gethostbyname. This image's local.conf appends ipv6 to DISTRO_FEATURES, so # the getaddrinfo arm is the live one -- but the scoring below is driven by # whether EITHER answered, so it stays right if that changes. resolve=$(sed -n 's/^SM RESOLVE //p' "$work/out") resolve_ai=$(sed -n 's/^SM RESOLVE-AI //p' "$work/out") gid1=$(sed -n 's/^SM GENID 1 = //p' "$work/out") shape=$(grep -F 'SM GENID-SHAPE ' "$work/out" 2>/dev/null | head -1) # The lookups answered SOMETHING -- either an address family or the # documented refusal. A blank line here means the probe did not reach this # section and every case below it would pass or fail for the wrong reason. if [ -n "$resolve" ]; then bst_ok libsm/resolve-reported else bst_notok libsm/resolve-reported "no SM RESOLVE line"; fi if [ -n "$resolve_ai" ]; then bst_ok libsm/resolve-ai-reported else bst_notok libsm/resolve-ai-reported "no SM RESOLVE-AI line"; fi # Which of the two governs is not knowable from here, so the scoring is on # their AGREEMENT rather than on a guess: both refusing means libSM cannot # build an id whichever arm it took, both answering means it can, and a # disagreement is its own case because it makes the shape unassertable. case "$resolve" in *"= null"*) r_ok=0 ;; *) r_ok=1 ;; esac case "$resolve_ai" in *"= null"*) a_ok=0 ;; *) a_ok=1 ;; esac if [ "$r_ok" -ne "$a_ok" ]; then # Not a libSM case at all -- the two resolver entry points disagreed about # this machine's own name. Reported, not scored against the library, and # the shape is left alone because it cannot be predicted. bst_notok libsm/resolve-arms-agree \ "getaddrinfo and gethostbyname disagree: '$(_bst_1line "$resolve_ai")' vs '$(_bst_1line "$resolve")'" bst_skip libsm/genid-shape "the two resolver arms disagree; no id shape is predictable" elif [ "$r_ok" -eq 0 ]; then # THE EXPECTED ARM ON THIS IMAGE. Both lookups refused, so # SmsGenerateClientID's documented answer is NULL -- and it is scored as a # pass BECAUSE the refusal above is what explains it. A NULL id with a # RESOLVED hostname falls into the branch below and is a red. bst_ok libsm/resolve-arms-agree bst_ok libsm/resolve-refused-as-expected check_eq libsm/genid-null-when-unresolvable null "$gid1" bst_skip libsm/genid-shape \ "the hostname does not resolve on this image, so there is no id to shape-check" else # The arm for an image whose own name resolves. The format is the # documented one, produced by one sprintf in sm_genid.c: # # "1" + address + %.13ld time + %.10ld pid + %.4d sequence # # where address is an address-code digit followed by the address in # LOWERCASE HEX -- '1' and 8 hex chars for IPv4, '6' and 32 for IPv6. So # the length follows the family: 1+9+13+10+4 = 37, or 1+33+13+10+4 = 61, # and which of the two getaddrinfo returns first is the image's business # rather than libSM's. Both arms are accepted; neither is a wildcard. # # hexdigits, NOT alldigits: the address half is hex, so an 'f' in it is # correct and a decimal-only check would red on a routable address. That # is not a hypothetical -- the build host's own 127.0.1.1 encodes as # 7f000101, and it is what caught this. bst_ok libsm/resolve-arms-agree bst_ok libsm/resolve-answered case "$shape" in "SM GENID-SHAPE len=37 prefix=1 addrcode=1 hexdigits=yes distinct=yes") \ bst_ok libsm/genid-shape ;; "SM GENID-SHAPE len=61 prefix=1 addrcode=6 hexdigits=yes distinct=yes") \ bst_ok libsm/genid-shape ;; *) bst_notok libsm/genid-shape "$(_bst_1line "$shape")" ;; esac fi # Whichever arm was taken, all three calls must have AGREED with each other # about whether an id can be made. A run where the first answered and the # third did not is a resolver that changed its mind mid-probe, and neither # arm above would notice. nulls=$(grep -c '^SM GENID [123] = null$' "$work/out" 2>/dev/null) case "${nulls:-x}" in 0|3) bst_ok libsm/genid-consistent ;; *) bst_notok libsm/genid-consistent "$nulls of 3 calls returned null" ;; esac # ------------------------------------------------------------------------- # 7. THE PROPERTY ENCODING ROUND TRIP. # # The fixture is what a real client sets at registration: Program as an # ARRAY8, RestartCommand and CloneCommand as LISTofARRAY8 with more than one # value each -- which is the case that exercises the value loop in the # encoder and in SmFreeProperty both. # # The encoder and decoder are the PROBE'S, mirroring SMlibint.h's # STORE_LISTOF_PROPERTY and sm_process.c's extractListofProperty, because # both of those are private to a connection's message path and SMlibint.h is # not installed. What that measures is the arithmetic on THIS ARM, where # every offset in it is 4 bytes wide, and it is the arithmetic every # SmcSetProperties libXt makes will run. # The size formula and the encoder are separate macros used at separate call # sites in the library -- the size decides the malloc and the store fills it # -- so a disagreement is a heap overrun in every real SmcSetProperties, and # it is invisible until one happens. Asserted as agreement rather than as a # byte count: the count moves if the fixture is edited, the agreement must # not. case "$(grep -F 'SM PROPS encode ' "$work/out" 2>/dev/null | head -1)" in *"agree=yes") bst_ok libsm/props-encode-size ;; "") bst_notok libsm/props-encode-size "no SM PROPS encode line" ;; *) bst_notok libsm/props-encode-size \ "$(_bst_1line "$(grep -F 'SM PROPS encode ' "$work/out" | head -1)")" ;; esac # XSMP is an 8-byte-aligned protocol and ICE rejects a message that is not, # so the total is a protocol requirement rather than a tidiness one. sm_line libsm/props-encode-aligned "SM PROPS encode-aligned " "yes" sm_line libsm/props-roundtrip "SM PROPS roundtrip " "count=3 equal=yes" # The names and value counts separately from the equality, so a red says # WHICH property came back wrong. equal=no with these two green means the # damage is in a value's bytes; a wrong count here means the length prefixes # walked off. sm_line libsm/props-roundtrip-names "SM PROPS roundtrip-names " \ "Program|RestartCommand|CloneCommand" sm_line libsm/props-roundtrip-vals "SM PROPS roundtrip-vals " "1|3|3" # ------------------------------------------------------------------------- # 8. THE TWO FREES A CLIENT CALLS DIRECTLY. # # SmFreeProperty walks name, type, every value, the value array and the # SmProp itself -- five allocations per property. A wrong struct offset on # this arm frees the wrong pointers, which is a FAULT rather than a wrong # answer, so what these cases assert is that the probe reached the line # after each one. The repetition is what makes a one-past-the-end free land # on a live chunk instead of on slack. sm_line libsm/free-decoded "SM FREE decoded = " "done" sm_line libsm/free-rounds "SM FREE rounds=2000 = " "done" sm_line libsm/free-null-property "SM FREE null-property = " "done" sm_line libsm/free-reasons "SM FREE reasons=2000 = " "done" # A count of zero with a real array, and a NULL array with a non-zero count. # The second is the arm a client takes when the manager sent no reasons at # all, and it is one `if' in sm_misc.c. sm_line libsm/free-reasons-degenerate "SM FREE reasons-degenerate " "= done" # ------------------------------------------------------------------------- # 9. THE TWO DOCUMENTED REFUSALS OF SmcOpenConnection. # # They must be DISTINGUISHABLE, and that is the assertion -- identical NULLs # would also come back from a build that gave up before doing anything. # libSM's own refusal, returned before ICE is touched at all. The sentence # is strncpy'd verbatim in sm_client.c, so it is asserted verbatim. sm_line libsm/open-no-session-manager "SM OPEN no-session-manager = " \ "null err='SESSION_MANAGER environment variable not defined' clientid=(null)" # The same branch by the other door: an EMPTY list is treated as an absent # one, not as an id to try. Without this case a build that only checked for # NULL would pass the case above and hand "" to ICE. sm_line libsm/open-empty-network-id "SM OPEN empty-network-id = " \ "null err='SESSION_MANAGER environment variable not defined' clientid=(null)" # AND THE ONE THAT REACHES ICE AND xtrans. The id names the unix transport # explicitly with host `unix' -- which skips SocketUNIXConnect's locality # check outright, so NO NAME IS RESOLVED -- and an absolute path that does # not exist, used verbatim as the socket path. connect(2) returns ENOENT, # xtrans maps that to TRANS_CONNECT_FAILED (a HARD failure, so ICE's # five-retry sleep loop is not entered), and libICE's connect.c reports it # with this sentence. A DIFFERENT sentence here is the interesting failure: # it means the call got further than it should have, or that libICE's own # port has moved under this one. sm_line libsm/open-dead-socket "SM OPEN dead-unix-socket = " \ "null err='Could not open network socket' clientid=(null)" # clientIdRet is documented to be set to NULL BEFORE anything else, on every # path including the earliest refusal. The probe poisons it with a non-NULL # sentinel first, so `(untouched)' above would mean a client that trusted the # documented behaviour is about to free a stack address. Asserted as a # census because every case above already carries its own clientid field. check_eq libsm/open-clientid-always-cleared 0 \ "$(grep -c 'clientid=(untouched)' "$work/out" 2>/dev/null)" else bst_notok libsm/probe-stage "cannot stage $SMT/libsm-probe" fi rm -rf "$work"