Fossil SCM
Attempt to limit the depth of recursion in markdown formatting.
Commit
a5835cac3b63d2d4fe5bfffcbe8bc99b64781124a87748c054eef2270cbf1cac
Parent
48276cfc811da9a…
1 file changed
+16
-10
+16
-10
| --- src/markdown.c | ||
| +++ src/markdown.c | ||
| @@ -153,10 +153,11 @@ | ||
| 153 | 153 | /* render -- structure containing one particular render */ |
| 154 | 154 | struct render { |
| 155 | 155 | struct mkd_renderer make; |
| 156 | 156 | struct Blob refs; |
| 157 | 157 | char_trigger active_char[256]; |
| 158 | + int iDepth; /* Depth of recursion */ | |
| 158 | 159 | int nBlobCache; /* Number of entries in aBlobCache */ |
| 159 | 160 | struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ |
| 160 | 161 | }; |
| 161 | 162 | |
| 162 | 163 | |
| @@ -299,14 +300,20 @@ | ||
| 299 | 300 | count(block_tags), |
| 300 | 301 | sizeof block_tags[0], |
| 301 | 302 | cmp_html_tag); |
| 302 | 303 | } |
| 303 | 304 | |
| 305 | +/* return true if recursion has gone too deep */ | |
| 306 | +static int too_deep(struct render *rndr){ | |
| 307 | + return rndr->iDepth>200; | |
| 308 | +} | |
| 304 | 309 | |
| 305 | -/* get a new working buffer from the cache or create one */ | |
| 310 | +/* get a new working buffer from the cache or create one. return NULL | |
| 311 | +** if failIfDeep is true and the depth of recursion has gone too deep. */ | |
| 306 | 312 | static struct Blob *new_work_buffer(struct render *rndr){ |
| 307 | 313 | struct Blob *ret; |
| 314 | + rndr->iDepth++; | |
| 308 | 315 | if( rndr->nBlobCache ){ |
| 309 | 316 | ret = rndr->aBlobCache[--rndr->nBlobCache]; |
| 310 | 317 | }else{ |
| 311 | 318 | ret = fossil_malloc(sizeof(*ret)); |
| 312 | 319 | } |
| @@ -316,10 +323,11 @@ | ||
| 316 | 323 | |
| 317 | 324 | |
| 318 | 325 | /* release the given working buffer back to the cache */ |
| 319 | 326 | static void release_work_buffer(struct render *rndr, struct Blob *buf){ |
| 320 | 327 | if( !buf ) return; |
| 328 | + rndr->iDepth--; | |
| 321 | 329 | blob_reset(buf); |
| 322 | 330 | if( rndr->nBlobCache < sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0]) ){ |
| 323 | 331 | rndr->aBlobCache[rndr->nBlobCache++] = buf; |
| 324 | 332 | }else{ |
| 325 | 333 | fossil_free(buf); |
| @@ -556,13 +564,13 @@ | ||
| 556 | 564 | } |
| 557 | 565 | if( data[i]==c |
| 558 | 566 | && data[i-1]!=' ' |
| 559 | 567 | && data[i-1]!='\t' |
| 560 | 568 | && data[i-1]!='\n' |
| 569 | + && !too_deep(rndr) | |
| 561 | 570 | ){ |
| 562 | 571 | work = new_work_buffer(rndr); |
| 563 | - if( !work ) return 0; | |
| 564 | 572 | parse_inline(work, rndr, data, i); |
| 565 | 573 | r = rndr->make.emphasis(ob, work, c, rndr->make.opaque); |
| 566 | 574 | release_work_buffer(rndr, work); |
| 567 | 575 | return r ? i+1 : 0; |
| 568 | 576 | } |
| @@ -594,13 +602,13 @@ | ||
| 594 | 602 | && data[i+1]==c |
| 595 | 603 | && i |
| 596 | 604 | && data[i-1]!=' ' |
| 597 | 605 | && data[i-1]!='\t' |
| 598 | 606 | && data[i-1]!='\n' |
| 607 | + && !too_deep(rndr) | |
| 599 | 608 | ){ |
| 600 | 609 | work = new_work_buffer(rndr); |
| 601 | - if( !work ) return 0; | |
| 602 | 610 | parse_inline(work, rndr, data, i); |
| 603 | 611 | r = rndr->make.double_emphasis(ob, work, c, rndr->make.opaque); |
| 604 | 612 | release_work_buffer(rndr, work); |
| 605 | 613 | return r ? i+2 : 0; |
| 606 | 614 | } |
| @@ -634,14 +642,14 @@ | ||
| 634 | 642 | |
| 635 | 643 | if( i+2<size |
| 636 | 644 | && data[i+1]==c |
| 637 | 645 | && data[i+2] == c |
| 638 | 646 | && rndr->make.triple_emphasis |
| 647 | + && !too_deep(rndr) | |
| 639 | 648 | ){ |
| 640 | 649 | /* triple symbol found */ |
| 641 | 650 | struct Blob *work = new_work_buffer(rndr); |
| 642 | - if( !work ) return 0; | |
| 643 | 651 | parse_inline(work, rndr, data, i); |
| 644 | 652 | r = rndr->make.triple_emphasis(ob, work, c, rndr->make.opaque); |
| 645 | 653 | release_work_buffer(rndr, work); |
| 646 | 654 | return r ? i+3 : 0; |
| 647 | 655 | }else if( i+1<size && data[i+1]==c ){ |
| @@ -990,14 +998,13 @@ | ||
| 990 | 998 | /* skip any amount of whitespace or newline */ |
| 991 | 999 | /* (this is much more laxist than original markdown syntax) */ |
| 992 | 1000 | while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } |
| 993 | 1001 | |
| 994 | 1002 | /* allocate temporary buffers to store content, link and title */ |
| 1003 | + title = new_work_buffer(rndr); | |
| 995 | 1004 | content = new_work_buffer(rndr); |
| 996 | 1005 | link = new_work_buffer(rndr); |
| 997 | - title = new_work_buffer(rndr); | |
| 998 | - if( !title ) return 0; | |
| 999 | 1006 | ret = 0; /* error if we don't get to the callback */ |
| 1000 | 1007 | |
| 1001 | 1008 | /* inline style link */ |
| 1002 | 1009 | if( i<size && data[i]=='(' ){ |
| 1003 | 1010 | size_t span_end = i; |
| @@ -1384,11 +1391,10 @@ | ||
| 1384 | 1391 | char *data, |
| 1385 | 1392 | size_t size |
| 1386 | 1393 | ){ |
| 1387 | 1394 | size_t beg, end, pre; |
| 1388 | 1395 | struct Blob *work = new_work_buffer(rndr); |
| 1389 | - if( !work ) work = ob; | |
| 1390 | 1396 | |
| 1391 | 1397 | beg = 0; |
| 1392 | 1398 | while( beg<size ){ |
| 1393 | 1399 | for(end=beg+1; end<size && data[end-1]!='\n'; end++); |
| 1394 | 1400 | pre = prefix_code(data+beg, end-beg); |
| @@ -1893,13 +1899,11 @@ | ||
| 1893 | 1899 | if( i<size && data[i]=='\n' ){ |
| 1894 | 1900 | align_size++; |
| 1895 | 1901 | |
| 1896 | 1902 | /* render the header row */ |
| 1897 | 1903 | head = new_work_buffer(rndr); |
| 1898 | - if( head ){ | |
| 1899 | - parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD); | |
| 1900 | - } | |
| 1904 | + parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD); | |
| 1901 | 1905 | |
| 1902 | 1906 | /* parse alignments if provided */ |
| 1903 | 1907 | if( col && (aligns=fossil_malloc(align_size * sizeof *aligns))!=0 ){ |
| 1904 | 1908 | for(i=0; i<align_size; i++) aligns[i] = 0; |
| 1905 | 1909 | col = 0; |
| @@ -2146,10 +2150,11 @@ | ||
| 2146 | 2150 | |
| 2147 | 2151 | /* filling the render structure */ |
| 2148 | 2152 | if( !rndrer ) return; |
| 2149 | 2153 | rndr.make = *rndrer; |
| 2150 | 2154 | rndr.nBlobCache = 0; |
| 2155 | + rndr.iDepth = 0; | |
| 2151 | 2156 | rndr.refs = empty_blob; |
| 2152 | 2157 | for(i=0; i<256; i++) rndr.active_char[i] = 0; |
| 2153 | 2158 | if( (rndr.make.emphasis |
| 2154 | 2159 | || rndr.make.double_emphasis |
| 2155 | 2160 | || rndr.make.triple_emphasis) |
| @@ -2204,10 +2209,11 @@ | ||
| 2204 | 2209 | if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); |
| 2205 | 2210 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); |
| 2206 | 2211 | if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque); |
| 2207 | 2212 | |
| 2208 | 2213 | /* clean-up */ |
| 2214 | + assert( rndr.iDepth==0 ); | |
| 2209 | 2215 | blob_reset(&text); |
| 2210 | 2216 | lr = (struct link_ref *)blob_buffer(&rndr.refs); |
| 2211 | 2217 | end = blob_size(&rndr.refs)/sizeof(struct link_ref); |
| 2212 | 2218 | for(i=0; i<end; i++){ |
| 2213 | 2219 | blob_reset(&lr[i].id); |
| 2214 | 2220 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -153,10 +153,11 @@ | |
| 153 | /* render -- structure containing one particular render */ |
| 154 | struct render { |
| 155 | struct mkd_renderer make; |
| 156 | struct Blob refs; |
| 157 | char_trigger active_char[256]; |
| 158 | int nBlobCache; /* Number of entries in aBlobCache */ |
| 159 | struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ |
| 160 | }; |
| 161 | |
| 162 | |
| @@ -299,14 +300,20 @@ | |
| 299 | count(block_tags), |
| 300 | sizeof block_tags[0], |
| 301 | cmp_html_tag); |
| 302 | } |
| 303 | |
| 304 | |
| 305 | /* get a new working buffer from the cache or create one */ |
| 306 | static struct Blob *new_work_buffer(struct render *rndr){ |
| 307 | struct Blob *ret; |
| 308 | if( rndr->nBlobCache ){ |
| 309 | ret = rndr->aBlobCache[--rndr->nBlobCache]; |
| 310 | }else{ |
| 311 | ret = fossil_malloc(sizeof(*ret)); |
| 312 | } |
| @@ -316,10 +323,11 @@ | |
| 316 | |
| 317 | |
| 318 | /* release the given working buffer back to the cache */ |
| 319 | static void release_work_buffer(struct render *rndr, struct Blob *buf){ |
| 320 | if( !buf ) return; |
| 321 | blob_reset(buf); |
| 322 | if( rndr->nBlobCache < sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0]) ){ |
| 323 | rndr->aBlobCache[rndr->nBlobCache++] = buf; |
| 324 | }else{ |
| 325 | fossil_free(buf); |
| @@ -556,13 +564,13 @@ | |
| 556 | } |
| 557 | if( data[i]==c |
| 558 | && data[i-1]!=' ' |
| 559 | && data[i-1]!='\t' |
| 560 | && data[i-1]!='\n' |
| 561 | ){ |
| 562 | work = new_work_buffer(rndr); |
| 563 | if( !work ) return 0; |
| 564 | parse_inline(work, rndr, data, i); |
| 565 | r = rndr->make.emphasis(ob, work, c, rndr->make.opaque); |
| 566 | release_work_buffer(rndr, work); |
| 567 | return r ? i+1 : 0; |
| 568 | } |
| @@ -594,13 +602,13 @@ | |
| 594 | && data[i+1]==c |
| 595 | && i |
| 596 | && data[i-1]!=' ' |
| 597 | && data[i-1]!='\t' |
| 598 | && data[i-1]!='\n' |
| 599 | ){ |
| 600 | work = new_work_buffer(rndr); |
| 601 | if( !work ) return 0; |
| 602 | parse_inline(work, rndr, data, i); |
| 603 | r = rndr->make.double_emphasis(ob, work, c, rndr->make.opaque); |
| 604 | release_work_buffer(rndr, work); |
| 605 | return r ? i+2 : 0; |
| 606 | } |
| @@ -634,14 +642,14 @@ | |
| 634 | |
| 635 | if( i+2<size |
| 636 | && data[i+1]==c |
| 637 | && data[i+2] == c |
| 638 | && rndr->make.triple_emphasis |
| 639 | ){ |
| 640 | /* triple symbol found */ |
| 641 | struct Blob *work = new_work_buffer(rndr); |
| 642 | if( !work ) return 0; |
| 643 | parse_inline(work, rndr, data, i); |
| 644 | r = rndr->make.triple_emphasis(ob, work, c, rndr->make.opaque); |
| 645 | release_work_buffer(rndr, work); |
| 646 | return r ? i+3 : 0; |
| 647 | }else if( i+1<size && data[i+1]==c ){ |
| @@ -990,14 +998,13 @@ | |
| 990 | /* skip any amount of whitespace or newline */ |
| 991 | /* (this is much more laxist than original markdown syntax) */ |
| 992 | while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } |
| 993 | |
| 994 | /* allocate temporary buffers to store content, link and title */ |
| 995 | content = new_work_buffer(rndr); |
| 996 | link = new_work_buffer(rndr); |
| 997 | title = new_work_buffer(rndr); |
| 998 | if( !title ) return 0; |
| 999 | ret = 0; /* error if we don't get to the callback */ |
| 1000 | |
| 1001 | /* inline style link */ |
| 1002 | if( i<size && data[i]=='(' ){ |
| 1003 | size_t span_end = i; |
| @@ -1384,11 +1391,10 @@ | |
| 1384 | char *data, |
| 1385 | size_t size |
| 1386 | ){ |
| 1387 | size_t beg, end, pre; |
| 1388 | struct Blob *work = new_work_buffer(rndr); |
| 1389 | if( !work ) work = ob; |
| 1390 | |
| 1391 | beg = 0; |
| 1392 | while( beg<size ){ |
| 1393 | for(end=beg+1; end<size && data[end-1]!='\n'; end++); |
| 1394 | pre = prefix_code(data+beg, end-beg); |
| @@ -1893,13 +1899,11 @@ | |
| 1893 | if( i<size && data[i]=='\n' ){ |
| 1894 | align_size++; |
| 1895 | |
| 1896 | /* render the header row */ |
| 1897 | head = new_work_buffer(rndr); |
| 1898 | if( head ){ |
| 1899 | parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD); |
| 1900 | } |
| 1901 | |
| 1902 | /* parse alignments if provided */ |
| 1903 | if( col && (aligns=fossil_malloc(align_size * sizeof *aligns))!=0 ){ |
| 1904 | for(i=0; i<align_size; i++) aligns[i] = 0; |
| 1905 | col = 0; |
| @@ -2146,10 +2150,11 @@ | |
| 2146 | |
| 2147 | /* filling the render structure */ |
| 2148 | if( !rndrer ) return; |
| 2149 | rndr.make = *rndrer; |
| 2150 | rndr.nBlobCache = 0; |
| 2151 | rndr.refs = empty_blob; |
| 2152 | for(i=0; i<256; i++) rndr.active_char[i] = 0; |
| 2153 | if( (rndr.make.emphasis |
| 2154 | || rndr.make.double_emphasis |
| 2155 | || rndr.make.triple_emphasis) |
| @@ -2204,10 +2209,11 @@ | |
| 2204 | if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); |
| 2205 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); |
| 2206 | if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque); |
| 2207 | |
| 2208 | /* clean-up */ |
| 2209 | blob_reset(&text); |
| 2210 | lr = (struct link_ref *)blob_buffer(&rndr.refs); |
| 2211 | end = blob_size(&rndr.refs)/sizeof(struct link_ref); |
| 2212 | for(i=0; i<end; i++){ |
| 2213 | blob_reset(&lr[i].id); |
| 2214 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -153,10 +153,11 @@ | |
| 153 | /* render -- structure containing one particular render */ |
| 154 | struct render { |
| 155 | struct mkd_renderer make; |
| 156 | struct Blob refs; |
| 157 | char_trigger active_char[256]; |
| 158 | int iDepth; /* Depth of recursion */ |
| 159 | int nBlobCache; /* Number of entries in aBlobCache */ |
| 160 | struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ |
| 161 | }; |
| 162 | |
| 163 | |
| @@ -299,14 +300,20 @@ | |
| 300 | count(block_tags), |
| 301 | sizeof block_tags[0], |
| 302 | cmp_html_tag); |
| 303 | } |
| 304 | |
| 305 | /* return true if recursion has gone too deep */ |
| 306 | static int too_deep(struct render *rndr){ |
| 307 | return rndr->iDepth>200; |
| 308 | } |
| 309 | |
| 310 | /* get a new working buffer from the cache or create one. return NULL |
| 311 | ** if failIfDeep is true and the depth of recursion has gone too deep. */ |
| 312 | static struct Blob *new_work_buffer(struct render *rndr){ |
| 313 | struct Blob *ret; |
| 314 | rndr->iDepth++; |
| 315 | if( rndr->nBlobCache ){ |
| 316 | ret = rndr->aBlobCache[--rndr->nBlobCache]; |
| 317 | }else{ |
| 318 | ret = fossil_malloc(sizeof(*ret)); |
| 319 | } |
| @@ -316,10 +323,11 @@ | |
| 323 | |
| 324 | |
| 325 | /* release the given working buffer back to the cache */ |
| 326 | static void release_work_buffer(struct render *rndr, struct Blob *buf){ |
| 327 | if( !buf ) return; |
| 328 | rndr->iDepth--; |
| 329 | blob_reset(buf); |
| 330 | if( rndr->nBlobCache < sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0]) ){ |
| 331 | rndr->aBlobCache[rndr->nBlobCache++] = buf; |
| 332 | }else{ |
| 333 | fossil_free(buf); |
| @@ -556,13 +564,13 @@ | |
| 564 | } |
| 565 | if( data[i]==c |
| 566 | && data[i-1]!=' ' |
| 567 | && data[i-1]!='\t' |
| 568 | && data[i-1]!='\n' |
| 569 | && !too_deep(rndr) |
| 570 | ){ |
| 571 | work = new_work_buffer(rndr); |
| 572 | parse_inline(work, rndr, data, i); |
| 573 | r = rndr->make.emphasis(ob, work, c, rndr->make.opaque); |
| 574 | release_work_buffer(rndr, work); |
| 575 | return r ? i+1 : 0; |
| 576 | } |
| @@ -594,13 +602,13 @@ | |
| 602 | && data[i+1]==c |
| 603 | && i |
| 604 | && data[i-1]!=' ' |
| 605 | && data[i-1]!='\t' |
| 606 | && data[i-1]!='\n' |
| 607 | && !too_deep(rndr) |
| 608 | ){ |
| 609 | work = new_work_buffer(rndr); |
| 610 | parse_inline(work, rndr, data, i); |
| 611 | r = rndr->make.double_emphasis(ob, work, c, rndr->make.opaque); |
| 612 | release_work_buffer(rndr, work); |
| 613 | return r ? i+2 : 0; |
| 614 | } |
| @@ -634,14 +642,14 @@ | |
| 642 | |
| 643 | if( i+2<size |
| 644 | && data[i+1]==c |
| 645 | && data[i+2] == c |
| 646 | && rndr->make.triple_emphasis |
| 647 | && !too_deep(rndr) |
| 648 | ){ |
| 649 | /* triple symbol found */ |
| 650 | struct Blob *work = new_work_buffer(rndr); |
| 651 | parse_inline(work, rndr, data, i); |
| 652 | r = rndr->make.triple_emphasis(ob, work, c, rndr->make.opaque); |
| 653 | release_work_buffer(rndr, work); |
| 654 | return r ? i+3 : 0; |
| 655 | }else if( i+1<size && data[i+1]==c ){ |
| @@ -990,14 +998,13 @@ | |
| 998 | /* skip any amount of whitespace or newline */ |
| 999 | /* (this is much more laxist than original markdown syntax) */ |
| 1000 | while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } |
| 1001 | |
| 1002 | /* allocate temporary buffers to store content, link and title */ |
| 1003 | title = new_work_buffer(rndr); |
| 1004 | content = new_work_buffer(rndr); |
| 1005 | link = new_work_buffer(rndr); |
| 1006 | ret = 0; /* error if we don't get to the callback */ |
| 1007 | |
| 1008 | /* inline style link */ |
| 1009 | if( i<size && data[i]=='(' ){ |
| 1010 | size_t span_end = i; |
| @@ -1384,11 +1391,10 @@ | |
| 1391 | char *data, |
| 1392 | size_t size |
| 1393 | ){ |
| 1394 | size_t beg, end, pre; |
| 1395 | struct Blob *work = new_work_buffer(rndr); |
| 1396 | |
| 1397 | beg = 0; |
| 1398 | while( beg<size ){ |
| 1399 | for(end=beg+1; end<size && data[end-1]!='\n'; end++); |
| 1400 | pre = prefix_code(data+beg, end-beg); |
| @@ -1893,13 +1899,11 @@ | |
| 1899 | if( i<size && data[i]=='\n' ){ |
| 1900 | align_size++; |
| 1901 | |
| 1902 | /* render the header row */ |
| 1903 | head = new_work_buffer(rndr); |
| 1904 | parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD); |
| 1905 | |
| 1906 | /* parse alignments if provided */ |
| 1907 | if( col && (aligns=fossil_malloc(align_size * sizeof *aligns))!=0 ){ |
| 1908 | for(i=0; i<align_size; i++) aligns[i] = 0; |
| 1909 | col = 0; |
| @@ -2146,10 +2150,11 @@ | |
| 2150 | |
| 2151 | /* filling the render structure */ |
| 2152 | if( !rndrer ) return; |
| 2153 | rndr.make = *rndrer; |
| 2154 | rndr.nBlobCache = 0; |
| 2155 | rndr.iDepth = 0; |
| 2156 | rndr.refs = empty_blob; |
| 2157 | for(i=0; i<256; i++) rndr.active_char[i] = 0; |
| 2158 | if( (rndr.make.emphasis |
| 2159 | || rndr.make.double_emphasis |
| 2160 | || rndr.make.triple_emphasis) |
| @@ -2204,10 +2209,11 @@ | |
| 2209 | if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); |
| 2210 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); |
| 2211 | if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque); |
| 2212 | |
| 2213 | /* clean-up */ |
| 2214 | assert( rndr.iDepth==0 ); |
| 2215 | blob_reset(&text); |
| 2216 | lr = (struct link_ref *)blob_buffer(&rndr.refs); |
| 2217 | end = blob_size(&rndr.refs)/sizeof(struct link_ref); |
| 2218 | for(i=0; i<end; i++){ |
| 2219 | blob_reset(&lr[i].id); |
| 2220 |