The code is already careful to not add items to lists that were
authorschwarze <schwarze@openbsd.org>
Thu, 18 Dec 2014 03:09:42 +0000 (03:09 +0000)
committerschwarze <schwarze@openbsd.org>
Thu, 18 Dec 2014 03:09:42 +0000 (03:09 +0000)
already closed.  In this respect, also consider lists closed
that have broken another block, their closure pending until the
end of the broken block.  This avoids syntax tree corruption
leading to a NULL pointer access found by jsg@ with afl.

regress/usr.bin/mandoc/mdoc/Bl/break.in
regress/usr.bin/mandoc/mdoc/Bl/break.out_ascii
regress/usr.bin/mandoc/mdoc/Bl/break.out_lint
usr.bin/mandoc/mdoc.h
usr.bin/mandoc/mdoc_macro.c

index a397042..4fe9246 100644 (file)
@@ -1,4 +1,4 @@
-.Dd November 16, 2012
+.Dd December 18, 2014
 .Dt BL-BREAK 1
 .Os OpenBSD
 .Sh NAME
@@ -13,6 +13,15 @@ before bracket
 .El
 after list
 .Bc
+in between
+.Bl -enum -offset indent
+.It
+before bracket
+.Bo inside both
+.El
+.It
+stray item
+.Bc
 after both
 .Sh EXAMPLES
 .Bl -enum -offset indent
@@ -23,6 +32,16 @@ inside both
 .El
 after display
 .Ed
+in between
+.Bl -enum -offset indent
+.It
+before display
+.Bd -ragged -offset indent
+inside both
+.El
+.It
+stray item
+.Ed
 after both
 .Sh CAVEATS
 .Bl -hang
index 4621805..ed14c25 100644 (file)
@@ -7,15 +7,25 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
      before both
 
            1.   before bracket [inside both after list]
+     in between
+
+           1.   before bracket [inside both
+                stray item]
      after both
 
 E\bEX\bXA\bAM\bMP\bPL\bLE\bES\bS
            1.   before display
 
                       inside both after display
+     in between
+
+           1.   before display
+
+                      inside both
+                      stray item
      after both
 
 C\bCA\bAV\bVE\bEA\bAT\bTS\bS
      before broken block [inside both after list]
 
-OpenBSD                        November 16, 2012                       OpenBSD
+OpenBSD                        December 18, 2014                       OpenBSD
index b99744f..70b3c98 100644 (file)
@@ -1,5 +1,9 @@
 mandoc: break.in:13:2: WARNING: blocks badly nested: El breaks Bo
-mandoc: break.in:23:2: WARNING: blocks badly nested: El breaks Bd
-mandoc: break.in:30:2: WARNING: blocks badly nested: El breaks It
-mandoc: break.in:29:25: ERROR: appending missing end of block: Bo
-mandoc: break.in:28:2: ERROR: appending missing end of block: Bl
+mandoc: break.in:21:2: WARNING: blocks badly nested: El breaks Bo
+mandoc: break.in:22:2: ERROR: skipping item outside list: It 
+mandoc: break.in:32:2: WARNING: blocks badly nested: El breaks Bd
+mandoc: break.in:41:2: WARNING: blocks badly nested: El breaks Bd
+mandoc: break.in:42:2: ERROR: skipping item outside list: It 
+mandoc: break.in:49:2: WARNING: blocks badly nested: El breaks It
+mandoc: break.in:48:25: ERROR: appending missing end of block: Bo
+mandoc: break.in:47:2: ERROR: appending missing end of block: Bl
index f19a805..24c2579 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mdoc.h,v 1.57 2014/11/28 19:25:03 schwarze Exp $ */
+/*     $OpenBSD: mdoc.h,v 1.58 2014/12/18 03:09:42 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -356,6 +356,7 @@ struct      mdoc_node {
        enum mdoct        tok; /* tok or MDOC__MAX if none */
        int               flags;
 #define        MDOC_VALID       (1 << 0) /* has been validated */
+#define        MDOC_BREAK       (1 << 1) /* has broken another block */
 #define        MDOC_EOS         (1 << 2) /* at sentence boundary */
 #define        MDOC_LINE        (1 << 3) /* first macro/text on line */
 #define        MDOC_SYNPRETTY   (1 << 4) /* SYNOPSIS-style formatting */
index b71a1fb..3dbd660 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mdoc_macro.c,v 1.113 2014/12/13 13:13:26 schwarze Exp $ */
+/*     $OpenBSD: mdoc_macro.c,v 1.114 2014/12/18 03:09:42 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
@@ -476,12 +476,16 @@ make_pending(struct mdoc_node *broken, enum mdoct tok,
        for (breaker = broken->parent; breaker; breaker = breaker->parent) {
 
                /*
-                * If the *broken block had already been broken before
-                * and we encounter its breaker, make the tok block
-                * pending on the inner breaker.
-                * Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]"
-                * becomes "[A broken=[B [C->B B] tok=A] C]"
-                * and finally "[A [B->A [C->B B] A] C]".
+                * If the *broken block (Z) is already broken and we
+                * encounter its breaker (B), make the tok block (A)
+                * pending on that inner breaker (B).
+                * Graphically, [A breaker=[B! broken=[Z->B B] tok=A] Z]
+                * becomes breaker=[A broken=[B! [Z->B B] tok=A] Z]
+                * and finally [A! [B!->A [Z->B B] A] Z].
+                * In these graphics, "->" indicates the "pending"
+                * pointer and "!" indicates the MDOC_BREAK flag.
+                * Each of the cases gets one additional pointer (B->A)
+                * and one additional flag (A!).
                 */
                if (breaker == broken->pending) {
                        broken = breaker;
@@ -495,31 +499,38 @@ make_pending(struct mdoc_node *broken, enum mdoct tok,
 
                /*
                 * Found the breaker.
-                * If another, outer breaker is already pending on
-                * the *broken block, we must not clobber the link
+                * If another, outer breaker (X) is already pending on
+                * the *broken block (B), we must not clobber the link
                 * to the outer breaker, but make it pending on the
-                * new, now inner breaker.
-                * Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]"
-                * becomes "[A breaker=[B->A broken=[C A] tok=B] C]"
-                * and finally "[A [B->A [C->B A] B] C]".
+                * new, now inner breaker (A).
+                * Graphically, [X! breaker=[A broken=[B->X X] tok=A] B]
+                * becomes [X! breaker=[A->X broken=[B X] tok=A] B]
+                * and finally [X! [A!->X [B->A X] A] B].
                 */
                if (broken->pending) {
                        struct mdoc_node *taker;
 
                        /*
-                        * If the breaker had also been broken before,
-                        * it cannot take on the outer breaker itself,
-                        * but must hand it on to its own breakers.
-                        * Graphically, this is the following situation:
-                        * "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]"
-                        * "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]"
+                        * If the inner breaker (A) is already broken,
+                        * too, it cannot take on the outer breaker (X)
+                        * but must hand it on to its own breakers (Y):
+                        * [X! [Y! breaker=[A->Y Y] broken=[B->X X] tok=A] B]
+                        * [X! take=[Y!->X brea=[A->Y Y] brok=[B X] tok=A] B]
+                        * and finally [X! [Y!->X [A!->Y Y] [B->A X] A] B].
                         */
                        taker = breaker;
                        while (taker->pending)
                                taker = taker->pending;
                        taker->pending = broken->pending;
                }
+
+               /*
+                * Now we have reduced the situation to the simplest
+                * case, which is just breaker=[A broken=[B tok=A] B]
+                * and becomes [A! [B->A A] B].
+                */
                broken->pending = breaker;
+               breaker->flags |= MDOC_BREAK;
                mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos,
                    "%s breaks %s", mdoc_macronames[tok],
                    mdoc_macronames[broken->tok]);
@@ -1065,8 +1076,8 @@ blk_full(MACRO_PROT_ARGS)
 
        if (tok == MDOC_It) {
                for (n = mdoc->last; n; n = n->parent)
-                       if (n->tok == MDOC_Bl &&
-                           ! (n->flags & MDOC_VALID))
+                       if (n->tok == MDOC_Bl && n->type == MDOC_BLOCK &&
+                           ! (n->flags & (MDOC_VALID | MDOC_BREAK)))
                                break;
                if (n == NULL) {
                        mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse,