From: Sergey Matveev Date: Wed, 11 Dec 2024 12:50:58 +0000 (+0300) Subject: Do not allow lonely EOC X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=5f9ba1f91a15abfe348f4eeddf98481b0a79ca80429f04fdfb301435c7890fcc;p=keks.git Do not allow lonely EOC --- diff --git a/cyac/lib/err.c b/cyac/lib/err.c index 414703b..0c638c2 100644 --- a/cyac/lib/err.c +++ b/cyac/lib/err.c @@ -38,8 +38,6 @@ YACErr2Str(const enum YACErr err) return "TAI64NonMinimal"; case YACErrMapBadKey: return "MapBadKey"; - case YACErrMapNoVal: - return "MapNoVal"; case YACErrMapUnordered: return "MapUnordered"; case YACErrNoMem: @@ -48,6 +46,8 @@ YACErr2Str(const enum YACErr err) return "UnsatisfiedSchema"; case YACErrDeepRecursion: return "DeepRecursion"; + case YACErrUnexpectedEOC: + return "UnexpectedEOC"; default: return "unknown"; } diff --git a/cyac/lib/err.h b/cyac/lib/err.h index d58fb33..8debb4d 100644 --- a/cyac/lib/err.h +++ b/cyac/lib/err.h @@ -31,8 +31,6 @@ // Invalid TAI64 attoseconds value. // @item YACErrMapBadKey // Either bad type of map's key, or it is empty. -// @item YACErrMapNoVal -// Missing value in a map. // @item YACErrMapUnordered // Unordered map keys. // @item YACErrNoMem @@ -41,6 +39,8 @@ // Unsatisfied structure's schema. // @item YACErrDeepRecursion // Too deep recursion involved during parsing. +// @item YACErrUnexpectedEOC +// Unexpected EOC met. // @end table // @end deftp enum YACErr { @@ -61,11 +61,11 @@ enum YACErr { YACErrTAI64IsLeap, YACErrTAI64NonMinimal, YACErrMapBadKey, - YACErrMapNoVal, YACErrMapUnordered, YACErrNoMem, YACErrUnsatisfiedSchema, YACErrDeepRecursion, + YACErrUnexpectedEOC, }; // TEXINFO: YACErr2Str diff --git a/cyac/lib/items.c b/cyac/lib/items.c index 84189cc..32e4b12 100644 --- a/cyac/lib/items.c +++ b/cyac/lib/items.c @@ -121,6 +121,7 @@ yacItemsParse( // NOLINT(misc-no-recursion) const unsigned char *buf, const size_t len, const bool allowContainers, + const bool expectEOC, const size_t recursionDepth) { if (recursionDepth >= parseMaxRecursionDepth) { @@ -135,6 +136,10 @@ yacItemsParse( // NOLINT(misc-no-recursion) #pragma clang diagnostic ignored "-Wswitch-enum" switch (items->list[item].atom.typ) { #pragma clang diagnostic pop + case YACItemEOC: + if (!expectEOC) { + return YACErrUnexpectedEOC; + } case YACItemList: { if (!allowContainers) { return YACErrUnknownType; @@ -145,7 +150,7 @@ yacItemsParse( // NOLINT(misc-no-recursion) size_t cur = 0; size_t idx = item; for (;;) { - err = yacItemsParse(items, off, buf, len, true, recursionDepth + 1); + err = yacItemsParse(items, off, buf, len, true, true, recursionDepth + 1); if (err != YACErrNo) { return err; } @@ -176,7 +181,7 @@ yacItemsParse( // NOLINT(misc-no-recursion) size_t prevKeyLen = 0; const unsigned char *prevKey = NULL; for (;;) { - err = yacItemsParse(items, off, buf, len, false, recursionDepth + 1); + err = yacItemsParse(items, off, buf, len, false, true, recursionDepth + 1); if (err != YACErrNo) { return err; } @@ -212,14 +217,11 @@ yacItemsParse( // NOLINT(misc-no-recursion) } prev = cur; idx = (items->len) - 1; - err = yacItemsParse(items, off, buf, len, true, recursionDepth + 1); + err = yacItemsParse(items, off, buf, len, true, false, recursionDepth + 1); if (err != YACErrNo) { return err; } cur = idx + 1; - if (items->list[cur].atom.typ == YACItemEOC) { - return YACErrMapNoVal; - } items->list[prev].next = cur; prev = cur; idx = (items->len) - 1; @@ -237,7 +239,7 @@ yacItemsParse( // NOLINT(misc-no-recursion) size_t cur = 0; bool eoc = false; while (!eoc) { - err = yacItemsParse(items, off, buf, len, false, recursionDepth + 1); + err = yacItemsParse(items, off, buf, len, false, true, recursionDepth + 1); if (err != YACErrNo) { return err; } @@ -291,7 +293,7 @@ YACItemsParse( // NOLINT(misc-no-recursion) const unsigned char *buf, const size_t len) { - return yacItemsParse(items, off, buf, len, true, 0); + return yacItemsParse(items, off, buf, len, true, false, 0); } bool diff --git a/cyac/lib/iter.c b/cyac/lib/iter.c index 906e63f..26e3e9d 100644 --- a/cyac/lib/iter.c +++ b/cyac/lib/iter.c @@ -112,7 +112,7 @@ YACIterMap( } (*off) += got; if (atom->typ == YACItemEOC) { - return YACErrMapNoVal; + return YACErrUnexpectedEOC; } err = cb(key, keyLen, false, 0, cbState, atom, off, buf, len); if (err != YACErrNo) { diff --git a/gyac/dec.go b/gyac/dec.go index 085b4a9..72adaa1 100644 --- a/gyac/dec.go +++ b/gyac/dec.go @@ -76,9 +76,9 @@ var ( ErrBadUTF8 = errors.New("invalid UTF-8") ErrMapBadKey = errors.New("map bad key") ErrMapUnordered = errors.New("map unordered") - ErrMapNoVal = errors.New("map no value") ErrBlobBadAtom = errors.New("blob unexpected atom") ErrBlobBadTerm = errors.New("blob bad terminator") + ErrUnexpectedEOC = errors.New("unexpected EOC") ) func AtomDecode(buf []byte) (item *Item, off int, err error) { @@ -323,7 +323,7 @@ func AtomDecode(buf []byte) (item *Item, off int, err error) { func itemDecode( buf []byte, - allowContainers bool, + allowContainers, expectEOC bool, recursionDepth int, ) (item *Item, tail []byte, err error) { if recursionDepth > ParseMaxRecursionDepth { @@ -338,6 +338,11 @@ func itemDecode( buf = buf[off:] tail = buf switch item.Typ() { + case ItemEOC: + if !expectEOC { + err = ErrUnexpectedEOC + return + } case ItemList: if !allowContainers { err = ErrUnknownType @@ -346,7 +351,7 @@ func itemDecode( var sub *Item var v []*Item for { - sub, buf, err = itemDecode(buf, true, recursionDepth+1) + sub, buf, err = itemDecode(buf, true, true, recursionDepth+1) tail = buf if err != nil { tail = buf @@ -368,7 +373,7 @@ func itemDecode( var sub *Item var keyPrev string for { - sub, buf, err = itemDecode(buf, false, recursionDepth+1) + sub, buf, err = itemDecode(buf, false, true, recursionDepth+1) tail = buf if err != nil { return @@ -395,15 +400,11 @@ func itemDecode( } keyPrev = s } - sub, buf, err = itemDecode(buf, true, recursionDepth+1) + sub, buf, err = itemDecode(buf, true, false, recursionDepth+1) tail = buf if err != nil { return } - if sub.T == byte(ItemEOC) { - err = ErrMapNoVal - return - } v[keyPrev] = sub } item.V = v @@ -418,7 +419,7 @@ func itemDecode( var sub *Item BlobCycle: for { - sub, buf, err = itemDecode(buf, false, recursionDepth+1) + sub, buf, err = itemDecode(buf, false, true, recursionDepth+1) tail = buf if err != nil { return @@ -454,5 +455,5 @@ func itemDecode( } func ItemDecode(buf []byte) (item *Item, tail []byte, err error) { - return itemDecode(buf, true, 0) + return itemDecode(buf, true, false, 0) } diff --git a/pyac/pyac.py b/pyac/pyac.py index 0492841..f9a64aa 100755 --- a/pyac/pyac.py +++ b/pyac/pyac.py @@ -280,11 +280,13 @@ def tai2utc(secs, leapsecUTCAllow=False): return secs - diff -def _loads(v, sets=False, leapsecUTCAllow=False, _allowContainers=True): +def _loads(v, sets=False, leapsecUTCAllow=False, _expectEOC=False, _allowContainers=True): if len(v) == 0: raise NotEnoughData(1) b = v[0] if b == TagEOC: + if not _expectEOC: + raise DecodeError("unexpected EOC") return _EOC, v[1:] if b == TagNIL: return None, v[1:] @@ -372,7 +374,7 @@ def _loads(v, sets=False, leapsecUTCAllow=False, _allowContainers=True): ret = [] v = v[1:] while True: - i, v = loads(v, sets=sets, leapsecUTCAllow=leapsecUTCAllow) + i, v = loads(v, sets=sets, leapsecUTCAllow=leapsecUTCAllow, _expectEOC=True) if i == _EOC: break ret.append(i) @@ -383,7 +385,7 @@ def _loads(v, sets=False, leapsecUTCAllow=False, _allowContainers=True): kPrev = "" allNILs = True while True: - k, v = _loads(v, _allowContainers=False) + k, v = _loads(v, _expectEOC=True, _allowContainers=False) if k == _EOC: break if not isinstance(k, str): @@ -392,9 +394,7 @@ def _loads(v, sets=False, leapsecUTCAllow=False, _allowContainers=True): if len(k) == 0: raise DecodeError("empty key") raise DecodeError("unsorted keys") - i, v = loads(v, sets=sets, leapsecUTCAllow=leapsecUTCAllow) - if i == _EOC: - raise DecodeError("unexpected EOC") + i, v = loads(v, sets=sets, leapsecUTCAllow=leapsecUTCAllow, _expectEOC=False) ret[k] = i kPrev = k if i is not None: @@ -409,7 +409,7 @@ def _loads(v, sets=False, leapsecUTCAllow=False, _allowContainers=True): v = v[1+8:] raws = [] while True: - i, v = _loads(v, _allowContainers=False) + i, v = _loads(v, _expectEOC=True, _allowContainers=False) if i is None: if len(v) < l: raise NotEnoughData(l-len(v)+1) diff --git a/pyac/tests/test_generic.py b/pyac/tests/test_generic.py index 25c286a..51b9654 100644 --- a/pyac/tests/test_generic.py +++ b/pyac/tests/test_generic.py @@ -53,3 +53,10 @@ class TestRaw(TestCase): @given(binary(min_size=1, max_size=1), binary(max_size=8)) def runTest(self, hdr: bytes, body: bytes) -> None: self.assertSequenceEqual(dumps(Raw(hdr[0], body)), hdr + body) + + +class TestLonelyEOC(TestCase): + def runTest(self) -> None: + with self.assertRaises(DecodeError) as err: + loads(b"\x00") + self.assertEqual(str(err.exception), "unexpected EOC")