+ /**
+ * The maximum length for a child element should be constrained to the
+ * length remaining in the first definite length element in the ancestor
+ * stack. If there is no definite length element in the ancestor stack,
+ * there's nothing to constrain the length of the child, so there's no
+ * further processing necessary.
+ *
+ * It's necessary to walk the ancestor stack, because it's possible to have
+ * definite length children that are part of an indefinite length element,
+ * which is itself part of an indefinite length element, and which is
+ * ultimately part of a definite length element. A simple example of this
+ * would be the handling of constructed OCTET STRINGs in BER encoding.
+ *
+ * This algorithm finds the first definite length element in the ancestor
+ * stack, if any, and if so, ensures that the length of the child element
+ * is consistent with the number of bytes remaining in the constraining
+ * ancestor element (that is, after accounting for any other sibling
+ * elements that may have been read).
+ *
+ * It's slightly complicated by the need to account both for integer
+ * underflow and overflow, as well as ensure that for indefinite length
+ * encodings, there's also enough space for the End-of-Contents (EOC)
+ * octets (Tag = 0x00, Length = 0x00, or two bytes).
+ */
+
+ /* Determine the maximum length available for this element by finding the
+ * first definite length ancestor, if any. */
+ sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(state);
+ while (parent && parent->indefinite) {
+ parent = sec_asn1d_get_enclosing_construct(parent);
+ }
+ /* If parent is null, state is either the outermost state / at the top of
+ * the stack, or the outermost state uses indefinite length encoding. In
+ * these cases, there's nothing external to constrain this element, so
+ * there's nothing to check. */
+ if (parent) {
+ unsigned long remaining = parent->pending;
+ parent = state;
+ do {
+ if (!sec_asn1d_check_and_subtract_length(&remaining, parent->consumed, state->top) ||
+ /* If parent->indefinite is true, parent->contents_length is
+ * zero and this is a no-op. */
+ !sec_asn1d_check_and_subtract_length(&remaining, parent->contents_length, state->top) ||
+ /* If parent->indefinite is true, then ensure there is enough
+ * space for an EOC tag of 2 bytes. */
+ (parent->indefinite && !sec_asn1d_check_and_subtract_length(&remaining, 2, state->top))) {
+ /* This element is larger than its enclosing element, which is
+ * invalid. */
+ return;
+ }
+ } while ((parent = sec_asn1d_get_enclosing_construct(parent)) &&
+ parent->indefinite);
+ }
+