Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package discount for openSUSE:Factory checked in at 2026-09-15 17:09:11 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/discount (Old) and /work/SRC/openSUSE:Factory/.discount.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "discount" Tue Sep 15 17:09:11 2026 rev:19 rq:1377732 version:3.0.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/discount/discount.changes 2026-04-23 17:09:40.081231997 +0200 +++ /work/SRC/openSUSE:Factory/.discount.new.383539/discount.changes 2026-09-15 17:09:32.875169593 +0200 @@ -1,0 +2,12 @@ +Sun Sep 13 14:21:36 UTC 2026 - Martin Pluskal <[email protected]> + +- Update to version 3.0.2.0: + * CVE-2026-4833: uncontrolled recursion in compile() on + deeply nested input, leading to stack exhaustion and + crash; the parser now caps nesting depth (default 200, + tunable via --with-recursion). + * Changes from version 3.0.1.3: keep contents of adjacent + blockquote chunks together, fenced code handling fixes. +- Drop Group: tags (deprecated). + +------------------------------------------------------------------- Old: ---- discount-3.0.1.2.tar.gz New: ---- discount-3.0.2.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ discount.spec ++++++ --- /var/tmp/diff_new_pack.2hVePo/_old 2026-09-15 17:09:34.223226011 +0200 +++ /var/tmp/diff_new_pack.2hVePo/_new 2026-09-15 17:09:34.226226136 +0200 @@ -19,20 +19,23 @@ %define sover 3 %bcond_with fenced_code Name: discount -Version: 3.0.1.2 +Version: 3.0.2.0 Release: 0 Summary: Markdown text to HTML converter License: BSD-3-Clause -Group: Productivity/Text/Convertors URL: https://www.pell.portland.or.us/~orc/Code/discount/ Source: https://github.com/Orc/%{name}/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz Patch1: discount-disable_ldconfig.patch BuildRequires: fdupes BuildRequires: pkgconfig -Provides: markdown +# Stays on update-alternatives: MultiMarkdown-6 registers the same +# generic /usr/bin/markdown name that way, so a one-sided move to +# libalternatives would fail installcheck (real-vs-ghost mismatch). +# Convert both sides together once MultiMarkdown-6 migrates. BuildRequires: update-alternatives Requires(post): update-alternatives Requires(postun): update-alternatives +Provides: markdown %description Discount is an implementation of John Gruber’s Markdown text to HTML language @@ -41,7 +44,6 @@ %package -n libmarkdown%{sover} Summary: Markdown text to HTML converter library -Group: Development/Libraries/C and C++ %description -n libmarkdown%{sover} Discount is an implementation of John Gruber’s Markdown text to HTML language @@ -50,7 +52,6 @@ %package -n libmarkdown-devel Summary: Markdown text to HTML converter library -Group: Development/Libraries/C and C++ Requires: libmarkdown%{sover} = %{version} %description -n libmarkdown-devel ++++++ discount-3.0.1.2.tar.gz -> discount-3.0.2.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/Makefile.in new/discount-3.0.2.0/Makefile.in --- old/discount-3.0.1.2/Makefile.in 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/Makefile.in 2026-08-29 05:34:06.000000000 +0200 @@ -31,7 +31,7 @@ github_flavoured.o setup.o tags.o html5.o \ pgm_options.o flags.o v2compat.o flagprocs.o \ @AMALLOC@ @H1TITLE@ -TESTFRAMEWORK=rep echo cols branch pandoc_headers space2nl +TESTFRAMEWORK=300 rep echo cols branch pandoc_headers space2nl # modules that markdown, makepage, mkd2html, &tc use COMMON=gethopt.o notspecial.o @@ -152,6 +152,11 @@ rep: rep.o $(LINK) -o rep rep.o +300.o : tools/300.c + $(BUILD) -c -o 300.o tools/300.c +300: 300.o + $(LINK) -o 300 300.o + branch.o: tools/branch.c config.h $(BUILD) -c -o branch.o tools/branch.c branch: branch.o diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/VERSION new/discount-3.0.2.0/VERSION --- old/discount-3.0.1.2/VERSION 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/VERSION 2026-08-29 05:34:06.000000000 +0200 @@ -1 +1 @@ -3.0.1.2 +3.0.2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/amalloc.c new/discount-3.0.2.0/amalloc.c --- old/discount-3.0.1.2/amalloc.c 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/amalloc.c 2026-08-29 05:34:06.000000000 +0200 @@ -7,6 +7,8 @@ #include <stdlib.h> #include "config.h" +extern void* memset(void*,int,size_t); + #define MAGIC 0x1f2e3d4c struct alist { int magic, size, index; int *end; struct alist *next, *last; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/basename.c new/discount-3.0.2.0/basename.c --- old/discount-3.0.1.2/basename.c 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/basename.c 2026-08-29 05:34:06.000000000 +0200 @@ -1,5 +1,5 @@ /* - * mkdio -- markdown front end input functions + * mkd_basename -- a url callback function that adds a prefix to an absolute path. * * Copyright (C) 2007 Jessica L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/configure.sh new/discount-3.0.2.0/configure.sh --- old/discount-3.0.1.2/configure.sh 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/configure.sh 2026-08-29 05:34:06.000000000 +0200 @@ -13,6 +13,7 @@ --container Build inside a container --pkg-config Install pkg-config(1) glue files --cxx-binding Install header files with c++ wrappers +--with-recursion=NNN how deeply things can nest before the become just text --github-checkbox[=input] Enable github-style checkboxes in lists (if =input, use <input checkbox>, otherwise use html ballot entities)' @@ -238,6 +239,11 @@ AC_SUB 'THEME' '#' fi +if [ -z "$WITH_RECURSION" ]; then + WITH_RECURSION=200 +fi +AC_DEFINE 'MAX_RECURSION' $WITH_RECURSION + if [ -z "$WITH_TABSTOPS" ]; then TABSTOP=4 elif [ "$WITH_TABSTOPS" -eq 1 ]; then diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/dumptree.c new/discount-3.0.2.0/dumptree.c --- old/discount-3.0.1.2/dumptree.c 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/dumptree.c 2026-08-29 05:34:06.000000000 +0200 @@ -22,6 +22,7 @@ switch (typ) { case WHITESPACE: return "whitespace"; case CODE : return "code"; + case FENCEDCODE: return "fenced code"; case QUOTE : return "quote"; case MARKUP : return "markup"; case HTML : return "html"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/generate.c new/discount-3.0.2.0/generate.c --- old/discount-3.0.1.2/generate.c 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/generate.c 2026-08-29 05:34:06.000000000 +0200 @@ -235,6 +235,7 @@ pushc(0, &sub); S(sub.in)--; + sub.recursion = f->recursion+1; text(&sub); ___mkd_emblock(&sub); @@ -1476,7 +1477,8 @@ || (f->last == 0) || ((ispunct(f->last) || isspace(f->last)) && f->last != ')') - || isthisspace(f,1) ) + || isthisspace(f,1) + || f->recursion > MAX_RECURSION ) Qchar(c,f); else { char *sup = cursor(f); @@ -1867,11 +1869,7 @@ Qstring(Begin[align], f); do { - if ( t->is_fenced ) { - text(f); - t = printfenced(t, f); - } - else if ( S(t->text) ) { + if ( S(t->text) ) { if ( t->next && S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' ' && T(t->text)[S(t->text)-1] == ' ' ) { @@ -2060,6 +2058,10 @@ printhtml(p->text, f); break; + case FENCEDCODE: + printfenced(p->text, f); + break; + case CODE: printcode(p->text, p->lang, f); break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/markdown.c new/discount-3.0.2.0/markdown.c --- old/discount-3.0.1.2/markdown.c 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/markdown.c 2026-08-29 05:34:06.000000000 +0200 @@ -632,8 +632,10 @@ static int -iscodefence(Line *r, int size, line_type kind, mkd_flag_t *flags) +iscodefence(Line *r, Line *orig, mkd_flag_t *flags) { + int kind = 0, size = 2, dle = 0; + if ( !is_flag_set(flags, MKD_FENCEDCODE) || is_flag_set(flags, MKD_STRICT) ) return 0; @@ -644,6 +646,15 @@ return 0; + if ( orig ) { + kind = orig->kind; + size = orig->count; + dle = orig->dle; + } + + if ( (dle>>2) != (r->dle>>2) ) + return 0; + if ( kind ) return (r->kind == kind) && (r->count >= size); else @@ -652,19 +663,20 @@ static Line * -fencedcodechunk(Line *first, mkd_flag_t *flags) +fencedcodechunk(Line *first, mkd_flag_t *flags, int *yes) { Line *r, *q; /* don't allow zero-length code fences */ - if ( (first->next == 0) || iscodefence(first->next, first->count, first->kind, flags) ) { + if ( (first->next == 0) || iscodefence(first->next, first, flags) ) { first->kind = chk_text; first->is_fenced = 0; if ( first->next) { first->next->kind = chk_text; first->next->is_fenced = 0; } + *yes = 0; return first->next; } @@ -672,7 +684,9 @@ * past the closing fence */ for ( r = first->next; r; r = r->next ) { - if ( iscodefence(r, first->count, first->kind, flags) ) { + if ( r->section_break ) + break; + if ( iscodefence(r, first, flags) ) { if (S(first->text) - first->count > 0) { char *lang_attr = T(first->text) + first->count; @@ -689,9 +703,13 @@ S(first->text) = S(r->text) = 0; r->is_fenced = 0; - return r->next; + *yes = 1; + q = r->next; + r->next = 0; + return q; } } + *yes = 0; first->is_fenced = 0; first->kind = chk_text; return first; @@ -742,9 +760,7 @@ Line *t, *next; for ( t = p->text; t ; t = next ) { - if ( iscodefence(t, t->count, 0, flags) ) - next = fencedcodechunk(t, flags); - else if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) { + if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) { p->align = centered(p->text, t); t->next = 0; return next; @@ -842,6 +858,8 @@ t = q; break; } + if ( q != t->next ) + t->section_break = 1; } if ( isdivmarker(p->text,0,flags) ) { char *prefix = "class"; @@ -1296,24 +1314,31 @@ ptr = consume(addfootnote(ptr, f), &eaten); previous_was_break = 1; } - else if (iscodefence(ptr, 2, 0, &(f->flags))) { - Line *more = ptr; - + else if ( previous_was_break && iscodefence(ptr, 0, &(f->flags)) ) { /* scoop up _everything_ in a fenced code block, including html & footnotes */ + int yes = 0; + Line *last = fencedcodechunk(ptr, &(f->flags), &yes ); - ATTACH(source,more); + if ( yes ) { + int dummy; - { Cache checkpoint = source; - while ( (more = more->next) && !iscodefence(more, ptr->count, ptr->kind, &(f->flags)) ) - ATTACH(source,more); - - if ( more ) - ptr = more; - else { - source = checkpoint; - ptr = ptr->next; - } + /* dump out any previously cached text + */ + uncache(&source, &d, f); + + p = Pp(&d, ptr, FENCEDCODE); + + ptr = consume(last, &dummy); + previous_was_break = 1; + } + else { + /* unterminated fenced code; treat as regular markdown & cache until the next + * fenced code, htl, or style block + */ + ATTACH(source, ptr); + previous_was_break = 0; + ptr = ptr->next; } } else { @@ -1409,9 +1434,14 @@ ptr = consume(ptr, ¶); + ++(f->recursion); while ( ptr ) { - if ( iscode(ptr) ) { + if ( f->recursion > MAX_RECURSION ) { + p = Pp(&d, ptr, MARKUP); + ptr = textblock(p, toplevel, &(f->flags) ); + } + else if ( iscode(ptr) ) { p = Pp(&d, ptr, CODE); if ( is_flag_set(&(f->flags), MKD_1_COMPAT) ) { @@ -1474,14 +1504,25 @@ ptr = htmlblock(p, tag, &unclosed); if ( ! unclosed ) { p->typ = HTML; + continue; } } - if ( unclosed ) { - ptr = textblock(p, toplevel, &(f->flags) ); - /* tables are a special kind of paragraph */ - if ( actually_a_table(f, p->text) ) - p->typ = TABLE; + + if ( iscodefence(p->text, 0, &(f->flags)) ) { + int yes = 0; + + ptr = fencedcodechunk(p->text, &(f->flags), &yes); + + if ( yes ) { + p->typ = FENCEDCODE; + continue; + } } + + ptr = textblock(p, toplevel, &(f->flags) ); + /* tables are a special kind of paragraph */ + if ( actually_a_table(f, p->text) ) + p->typ = TABLE; } if ( (para||toplevel) && !p->align ) p->align = PARA; @@ -1494,6 +1535,7 @@ p->align = PARA; } + (f->recursion)--; return T(d); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/markdown.h new/discount-3.0.2.0/markdown.h --- old/discount-3.0.1.2/markdown.h 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/markdown.h 2026-08-29 05:34:06.000000000 +0200 @@ -85,6 +85,7 @@ int has_pipechar; /* line contains a | */ int is_checked; line_type kind; + int section_break; /* paragraph break in blockquote */ int is_fenced; /* line inside a fenced code block (ick) */ char *fence_class; /* fenced code class (ick) */ int count; @@ -102,7 +103,7 @@ char *label; /* toc label, uniqued */ char *ident; /* %id% tag for QUOTE */ char *lang; /* lang attribute for CODE */ - enum { WHITESPACE=0, CODE, QUOTE, MARKUP, + enum { WHITESPACE=0, CODE, FENCEDCODE, QUOTE, MARKUP, HTML, STYLE, DL, UL, OL, AL, LISTITEM, HDR, HR, TABLE, SOURCE } typ; enum { IMPLICIT=0, PARA, CENTER} align; @@ -195,6 +196,7 @@ Qblock Q; char last; /* last text character added to out */ int isp; + int recursion; struct escaped *esc; char *ref_prefix; struct footnote_list *footnotes; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/tests/codeblock.t new/discount-3.0.2.0/tests/codeblock.t --- old/discount-3.0.1.2/tests/codeblock.t 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/tests/codeblock.t 2026-08-29 05:34:06.000000000 +0200 @@ -50,19 +50,17 @@ still code! ~~~' \ - '<p><pre><code>code! + '<pre><code>code! still code! -</code></pre> -</p>' +</code></pre>' try -ffencedcode 'fenced code block' \ '~~~ code! ~~~' \ - '<p><pre><code>code! -</code></pre> -</p>' + '<pre><code>code! +</code></pre>' try -ffencedcode 'fenced code block in list' \ '1. ~~~ @@ -78,9 +76,21 @@ '>~~~ code ~~~' \ -'<blockquote><p><pre><code>code +'<blockquote><pre><code>code </code></pre> -</p></blockquote>' +</blockquote>' + +try -ffencedcode 'fenced code block in two adjacent blockquote chunks' \ +'>~~~~ +> not code + +> still not code +> ~~~~ ' \ +'<blockquote><p>~~~~ +not code</p> + +<p>still not code +~~~~</p></blockquote>' try -ffencedcode 'unterminated fenced code block' \ '~~~ @@ -94,11 +104,10 @@ code with tildes ~~~ ~~~~~' \ -'<p><pre><code>~~~ +'<pre><code>~~~ code with tildes ~~~ -</code></pre> -</p>' +</code></pre>' try -ffencedcode 'paragraph with trailing fenced block' \ 'text text text @@ -108,17 +117,16 @@ ~~~' \ '<p>text text text text text text -<pre><code>code code code? -</code></pre> -</p>' +~~~ +code code code? +~~~</p>' try -ffencedcode 'fenced code blocks with backtick delimiters' \ '``` code ```' \ -'<p><pre><code>code -</code></pre> -</p>' +'<pre><code>code +</code></pre>' try -ffencedcode 'fenced code block with mismatched delimters' \ '``` @@ -132,57 +140,50 @@ '```lang code ```' \ -'<p><pre><code class="lang">code -</code></pre> -</p>' +'<pre><code class="lang">code +</code></pre>' try -ffencedcode 'fenced code block with lang-name attribute' \ '```lang-name code ```' \ -'<p><pre><code class="lang-name">code -</code></pre> -</p>' +'<pre><code class="lang-name">code +</code></pre>' try -ffencedcode 'fenced code block with lang_name attribute' \ '```lang_name code ```' \ -'<p><pre><code class="lang_name">code -</code></pre> -</p>' +'<pre><code class="lang_name">code +</code></pre>' try -ffencedcode 'fenced code block with lang attribute and space' \ '``` lang code ```' \ -'<p><pre><code class="lang">code -</code></pre> -</p>' +'<pre><code class="lang">code +</code></pre>' try -ffencedcode 'fenced code block with lang attribute and multiple spaces' \ '``` lang code ```' \ -'<p><pre><code class="lang">code -</code></pre> -</p>' +'<pre><code class="lang">code +</code></pre>' try -ffencedcode 'fenced code block with lang-name attribute and space' \ '``` lang-name code ```' \ -'<p><pre><code class="lang-name">code -</code></pre> -</p>' +'<pre><code class="lang-name">code +</code></pre>' try -ffencedcode 'fenced code block with lang_name attribute and space' \ '``` lang_name code ```' \ -'<p><pre><code class="lang_name">code -</code></pre> -</p>' +'<pre><code class="lang_name">code +</code></pre>' try -ffencedcode 'fenced code block with blank line in the middle' \ '``` @@ -190,20 +191,18 @@ sailor ```' \ -'<p><pre><code>hello +'<pre><code>hello sailor -</code></pre> -</p>' +</code></pre>' try -ffencedcode 'fenced code block with html in the middle' \ '~~~~ <h1>hello, sailor</h1> ~~~~' \ -'<p><pre><code><h1>hello, sailor</h1> -</code></pre> -</p>' +'<pre><code><h1>hello, sailor</h1> +</code></pre>' try -ffencedcode 'fenced code block with trailing spaces in list item' \ '1. ~~~~ @@ -229,9 +228,8 @@ ~~~~' \ '<p>foo</p> -<p><pre><code>bar -</code></pre> -</p>' +<pre><code>bar +</code></pre>' try -ffencedcode 'checkline misparse as fenced code' \ @@ -241,10 +239,9 @@ ``` ' \ '<p><a href="#code"><code>label</code></a> -<pre><code class="class">content -</code></pre> -</p>' - +<code>class +content +</code></p>' summary $0 exit $rc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/tests/crash.t new/discount-3.0.2.0/tests/crash.t --- old/discount-3.0.1.2/tests/crash.t 2025-09-08 18:51:18.000000000 +0200 +++ new/discount-3.0.2.0/tests/crash.t 2026-08-29 05:34:06.000000000 +0200 @@ -76,21 +76,10 @@ ./rep '#header' ' ' 400 | try -T 'a header with a bunch of trailing spaces' -heredoc "$RESULT" -RESULT='<table> -<thead> -<tr> -<th>: Y:</th> -</tr> -</thead> -<tbody> -<tr> -<td></td> -</tr> -<tr> -<td></td> -</tr> -</tbody> -</table>' +RESULT='<p>: Y:| +<code>| +| +</code>|</p>' cat << \EOF | try '-F 0x03000000' 'random input that looks like a table' -heredoc "$RESULT" : Y:| diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/tests/limits.t new/discount-3.0.2.0/tests/limits.t --- old/discount-3.0.1.2/tests/limits.t 1970-01-01 01:00:00.000000000 +0100 +++ new/discount-3.0.2.0/tests/limits.t 2026-08-29 05:34:06.000000000 +0200 @@ -0,0 +1,29 @@ +. tests/functions.sh + +title 'recursion limits' +rc=0 + +limits() { + try_header "$1" + + res=`./300 400 $3 $4 $5 | ./markdown | tr -dc "$2" | wc -c` + + if (( $res > 0 )); then + # it worked + test $VERBOSE && ./echo " ok" + else + test "$VERBOSE" && ./echo "failed" + rc=1 + fi + +} + + +limits "superscripts" "()" "2^(" "A" ")" + +limits "list items" "1" "1. " "item" + +limits "blockquotes" ">" "> " "item" + +summary $0 +exit $rc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/discount-3.0.1.2/tools/300.c new/discount-3.0.2.0/tools/300.c --- old/discount-3.0.1.2/tools/300.c 1970-01-01 01:00:00.000000000 +0100 +++ new/discount-3.0.2.0/tools/300.c 2026-08-29 05:34:06.000000000 +0200 @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> + +int +main(argc, argv) +int argc; +char **argv; +{ + int reps; + int i; + char *pattern1, *text = NULL, *pattern2 = NULL; + + if ( argc < 3 ) { + fprintf(stderr, "usage: 300 reps pattern1 [text [pattern2]]\n"); + exit(1); + } + + + reps = atoi(argv[1]); + pattern1 = argv[2]; + if ( argc > 3 ) + text = argv[3]; + if ( argc > 4 ) + pattern2 = argv[4]; + + for ( i=0; i < reps; i++ ) + printf("%s", pattern1); + + if ( text ) + printf(" %s ", text); + + if ( pattern2 ) + for ( i=0; i < reps; i++ ) + printf("%s", pattern2); + + putchar('\n'); + exit(0); +}
