]> Cypherpunks repositories - keks.git/commitdiff
Do not allow lonely EOC
authorSergey Matveev <stargrave@stargrave.org>
Wed, 11 Dec 2024 12:50:58 +0000 (15:50 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Wed, 11 Dec 2024 13:52:22 +0000 (16:52 +0300)
cyac/lib/err.c
cyac/lib/err.h
cyac/lib/items.c
cyac/lib/iter.c
gyac/dec.go
pyac/pyac.py
pyac/tests/test_generic.py

index 414703b4db94e5631bcd40f0a4bc5cfde17c016590da56cc091bb593fcf113ed..0c638c279be565af734945a0cd72fa3727258e4461a1388ef9d465f029fd9945 100644 (file)
@@ -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";
     }
index d58fb3338dd639f9a09a7d614699de5388edfdeb2965f51d05cc7c27527e3141..8debb4d70fbb93890eb66c5aff1a46d87f6205c1a033f8cda127c3ebd2b903b3 100644 (file)
@@ -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
index 84189cca8b78696d400cda75df19330f04540329f1d5d687f818d6573d7acfd8..32e4b125a58e748188faf60750b128090bda88274325da7661502890b2924ae7 100644 (file)
@@ -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
index 906e63f37df75faa1f2267982772b56a77e08cd4d59261b23c7252d262365174..26e3e9de1670b536b457e918e51723656982d14dde7a7102127a98aee67d5235 100644 (file)
@@ -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) {
index 085b4a9cbefd3ccca3e2e3161709feab669392a76d9663e6a9ca912a80166a9c..72adaa17f3692d24dad90989da7235e35500c7b5057735b65e8b546d31151a49 100644 (file)
@@ -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)
 }
index 0492841506bb65fcb50686822e0557a7bd6f909281173ab8bf0cff8c32c6cf69..f9a64aa8d16d829e9400fd4fee0b1f35280a9a6347cdf27aa0fe0ddf60a0886b 100755 (executable)
@@ -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)
index 25c286a6a877352138c98317c5e71fd67c16de832684f15c66deda21a91ea05c..51b9654b282371bd927afa060a856f743320c55b94412d23ed2f779d1b978c8e 100644 (file)
@@ -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")