I found 2 minor issues in the handling of sequences/sets in ober_read_element(): 1) An empty sequence/set (which is basically always) unconditionally creates an (uninitialised) sub-element. Add the same length check used to check the next element 2) For each sub-element r is only checked for -1, but not if it overflows the length of the sequence itself. This is not a big risk since each sub-element is length-checked against the buffer availability and simply returns an ECANCELED, which would be no worse memory-wise than sending an extremely large packet.
While here, only having the NULL of the comparison on the next line annoyed me. OK? martijn@ Index: ber.c =================================================================== RCS file: /cvs/src/lib/libutil/ber.c,v retrieving revision 1.23 diff -u -p -r1.23 ber.c --- ber.c 21 Oct 2021 08:17:33 -0000 1.23 +++ ber.c 2 Nov 2022 06:28:33 -0000 @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc break; case BER_TYPE_SEQUENCE: case BER_TYPE_SET: - if (elm->be_sub == NULL) { + if (len > 0 && elm->be_sub == NULL) { if ((elm->be_sub = ober_get_element(0)) == NULL) return -1; } @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc return -1; } r = ober_read_element(ber, next); - if (r == -1) + if (r == -1) { + /* sub-element overflows sequence/set */ + if (errno == ECANCELED) + errno = EINVAL; return -1; + } + if (r > len) { + errno = EINVAL; + return -1; + } elements++; len -= r; if (len > 0 && next->be_next == NULL) { - if ((next->be_next = ober_get_element(0)) == - NULL) + next->be_next = ober_get_element(0); + if (next->be_next == NULL) return -1; } next = next->be_next;