| Document | draft-fossil-sync-protocol-00 |
| Section | 8.1 — Clone Protocol Version (server handling of a SEQNO that is not digit-only) |
| Type | Compatibility note (no requirement on a conforming implementation changes) |
| Status | Applied in draft-fossil-sync-protocol-01 (2026-10-12) |
| Reported | 2026-10-11 |
| Submitter | Dan Mestas |
| Found via | go-libfossil's sync conformance suite: fossil 2.28 [52445a27f1] answers clone 3 -1 differently on macOS arm64 and Linux x64 builds |
1. Summary
§8.1 assigns error invalid clone sequence number only to a digit-only
SEQNO whose value is zero, and says the rule "does not assign that fatal
outcome to wrong arity, noninteger VERSION, or a SEQNO that fails
digit-only recognition". It says nothing about what such a SEQNO does
get. In Fossil 2.28 the answer is undefined: the server reads an
uninitialized variable. The same request, clone 3 -1, gets
clone_seqno 0 from a macOS arm64 build and
error invalid clone sequence number from a Linux x64 build.
The draft's grammar (clone-card = "clone" SP decimal SP decimal LF) and
its clone loop (§8.2: SEQNO starts at 1 and is replaced only by a decimal
clone_seqno) already keep a conforming client from sending such a card,
so no requirement changes. The note records that Fossil gives no stable
answer, so that neither tests nor implementations treat either observed
reply as the reference behavior.
2. Location
- §8.1, second paragraph (~lines 1493-1500): "This rule does not assign that fatal outcome to wrong arity, noninteger VERSION, or a SEQNO that fails digit-only recognition or does not yield that condition."
3. The divergence
Fossil 2.28, src/xfer.c:1550-1570, in the clone handler:
int seqno, max; /* seqno is not initialized */
...
blob_is_int(&xfer.aToken[2], &seqno); /* return value ignored */
if( seqno<=0 ){
xfer_fatal_error("invalid clone sequence number");
...
}
...
if( seqno>max ) seqno = 0;
@ clone_seqno %d(seqno)
blob_is_int() (src/blob.c:1057-1071) stores a value only when every byte
is an ASCII digit; for -1, abc or 1x it returns 0 and leaves seqno
as whatever the stack held. A non-positive leftover gives the fatal error
and rollback; a positive one greater than the largest RID gives no cfile
and clone_seqno 0; a positive one within range would serve artifacts
from an arbitrary RID.
Observed on this machine (fossil 2.28 [52445a27f1], Homebrew, macOS arm64),
fossil http with an application/x-fossil-debug request, three runs each:
| SEQNO | Reply |
|---|---|
0 |
error invalid\sclone\ssequence\snumber |
-1 |
clone_seqno 0, push SERVERCODE PROJECTCODE, timestamp |
abc |
same as -1 |
1x |
same as -1 |
The Linux x64 reply (error invalid\sclone\ssequence\snumber for
clone 3 -1) is from the go-libfossil suite's run on that platform and was
not reproduced here.
4. Impact
None for a conforming client, which cannot send the card. A test that pins either reply for a non-digit SEQNO passes against one Fossil build and fails against another, and a server implementer looking to Fossil for the "compatible" answer finds none.
5. Correction
In §8.1, append to the second paragraph:
parsing later request cards. This rule does not assign that fatal
outcome to wrong arity, noninteger VERSION, or a SEQNO that fails
digit-only recognition or does not yield that condition.
+ Fossil gives no stable reply to a SEQNO that is not digit-only: it
+ tests an uninitialized value, so builds differ between the fatal
+ error, an empty clone_seqno 0 reply, and serving from an arbitrary
+ RID. Such a card does not match the clone-card grammar (Appendix B)
+ and is never produced by the client loop of Section 8.2; clients
+ MUST send a positive decimal SEQNO, and neither a client nor a test
+ can rely on any particular reply to another value.
6. Rationale
- No new obligation. The grammar and the clone loop already confine a client to a positive decimal SEQNO; the MUST makes that explicit at the card's definition, where a reader looks for it.
- Removes a false oracle. Recording that Fossil's reply depends on an uninitialized variable stops either platform's output from being taken as the protocol's answer.
7. Verification
- Fossil 2.28 [52445a27f1], macOS arm64: the table above, three runs per
SEQNO,
nobodyholdingog. - Source references are to
src/xfer.candsrc/blob.cat tagversion-2.28. - Not verified here: the Linux x64 reply, taken from the go-libfossil suite's report.
Prepared against draft-fossil-sync-protocol-00. The RFC remains the oracle; the reference implementation is used only to confirm the observable behavior.