* [poky][dunfell][PATCH] busybox: Fix multiple security issues in awk
@ 2021-12-09 5:12 Sana Kazi
2021-12-09 16:15 ` [OE-core] " Steve Sakoman
0 siblings, 1 reply; 3+ messages in thread
From: Sana Kazi @ 2021-12-09 5:12 UTC (permalink / raw)
To: openembedded-core; +Cc: Sana Kazi
CVE-2021-423xx-awk.patch fixes below listed CVEs for busybox:
CVE-2021-42378, CVE-2021-42379, CVE-2021-42380, CVE-2021-42381,
CVE-2021-42382, CVE-2021-42384, CVE-2021-42385, CVE-2021-42386
Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com>
Signed-off-by: Sana Kazi <sanakazisk19@gmail.com>
---
meta/recipes-core/busybox/busybox_1.31.1.bb | 1 +
.../busybox/files/CVE-2021-423xx-awk.patch | 203 ++++++++++++++++++
2 files changed, 204 insertions(+)
create mode 100644 meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch
diff --git a/meta/recipes-core/busybox/busybox_1.31.1.bb b/meta/recipes-core/busybox/busybox_1.31.1.bb
index d9d5f4f96b..1bb0c028ac 100644
--- a/meta/recipes-core/busybox/busybox_1.31.1.bb
+++ b/meta/recipes-core/busybox/busybox_1.31.1.bb
@@ -52,6 +52,7 @@ SRC_URI = "https://busybox.net/downloads/busybox-${PV}.tar.bz2;name=tarball \
file://0001-hwclock-make-glibc-2.31-compatible.patch \
file://0001-decompress_gunzip-Fix-DoS-if-gzip-is-corrupt.patch \
file://0001-mktemp-add-tmpdir-option.patch \
+ file://CVE-2021-423xx-awk.patch \
"
SRC_URI_append_libc-musl = " file://musl.cfg "
diff --git a/meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch b/meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch
new file mode 100644
index 0000000000..49296354cb
--- /dev/null
+++ b/meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch
@@ -0,0 +1,203 @@
+Description: fix multiple security issues in awk
+Origin: backported awk.c from busybox 1.34.1
+
+CVE: CVE-2021-42378
+CVE: CVE-2021-42379
+CVE: CVE-2021-42380
+CVE: CVE-2021-42381
+CVE: CVE-2021-42382
+CVE: CVE-2021-42384
+CVE: CVE-2021-42385
+CVE: CVE-2021-42386
+
+Upstream-Status: Backport [https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/busybox/1:1.30.1-6ubuntu3.1/busybox_1.30.1-6ubuntu3.1.debian.tar.xz]
+
+Comment: Refreshed first hunk and removed few hunks as they are already present in source.
+
+Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com>
+
+--- a/editors/awk.c
++++ b/editors/awk.c
+@@ -272,7 +272,8 @@
+ /* if previous token class is CONCAT1 and next is CONCAT2, concatenation */
+ /* operator is inserted between them */
+ #define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM \
+- | TC_STRING | TC_NUMBER | TC_UOPPOST)
++ | TC_STRING | TC_NUMBER | TC_UOPPOST \
++ | TC_LENGTH)
+ #define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE)
+
+ #define OF_RES1 0x010000
+@@ -401,7 +405,7 @@ static const char tokenlist[] ALIGN1 =
+
+ #define OC_B OC_BUILTINCVE-2021-423xx-awk.patch
+
+-static const uint32_t tokeninfo[] = {
++static const uint32_t tokeninfo[] ALIGN4 = {
+ 0,
+ 0,
+ OC_REGEXP
+@@ -1067,8 +1071,10 @@ static uint32_t next_token(uint32_t expe
+ const uint32_t *ti;
+
+ if (t_rollback) {
++ debug_printf_parse("%s: using rolled-back token\n", __func__);
+ t_rollback = FALSE;
+ } else if (concat_inserted) {
++ debug_printf_parse("%s: using concat-inserted token\n", __func__);
+ concat_inserted = FALSE;
+ t_tclass = save_tclass;
+ t_info = save_info;
+@@ -1197,7 +1203,11 @@ static uint32_t next_token(uint32_t expe
+ goto readnext;
+
+ /* insert concatenation operator when needed */
+- if ((ltclass & TC_CONCAT1) && (tc & TC_CONCAT2) && (expected & TC_BINOP)) {
++ debug_printf_parse("%s: %x %x %x concat_inserted?\n", __func__,
++ (ltclass & TC_CONCAT1), (tc & TC_CONCAT2), (expected & TC_BINOP));
++ if ((ltclass & TC_CONCAT1) && (tc & TC_CONCAT2) && (expected & TC_BINOP)
++ && !(ltclass == TC_LENGTH && tc == TC_SEQSTART) /* but not for "length(..." */
++ ) {
+ concat_inserted = TRUE;
+ save_tclass = tc;
+ save_info = t_info;
+@@ -1205,6 +1215,7 @@ static uint32_t next_token(uint32_t expe
+ t_info = OC_CONCAT | SS | P(35);
+ }
+
++ debug_printf_parse("%s: t_tclass=tc=%x\n", __func__, t_tclass);
+ t_tclass = tc;
+ }
+ ltclass = t_tclass;
+@@ -1215,6 +1226,7 @@ static uint32_t next_token(uint32_t expe
+ EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
+ }
+
++ debug_printf_parse("%s: returning, ltclass:%x t_double:%f\n", __func__, ltclass, t_double);
+ return ltclass;
+ #undef concat_inserted
+ #undef save_tclass
+@@ -1279,7 +1291,7 @@ static node *parse_expr(uint32_t iexp)
+ glptr = NULL;
+
+ } else if (tc & (TC_BINOP | TC_UOPPOST)) {
+- debug_printf_parse("%s: TC_BINOP | TC_UOPPOST\n", __func__);
++ debug_printf_parse("%s: TC_BINOP | TC_UOPPOST tc:%x\n", __func__, tc);
+ /* for binary and postfix-unary operators, jump back over
+ * previous operators with higher priority */
+ vn = cn;
+@@ -1346,8 +1359,10 @@ static node *parse_expr(uint32_t iexp)
+ v = cn->l.v = xzalloc(sizeof(var));
+ if (tc & TC_NUMBER)
+ setvar_i(v, t_double);
+- else
++ else {
+ setvar_s(v, t_string);
++ xtc &= ~TC_UOPPOST; /* "str"++ is not allowed */
++ }
+ break;
+
+ case TC_REGEXP:
+@@ -1383,7 +1398,12 @@ static node *parse_expr(uint32_t iexp)
+
+ case TC_LENGTH:
+ debug_printf_parse("%s: TC_LENGTH\n", __func__);
+- next_token(TC_SEQSTART | TC_OPTERM | TC_GRPTERM);
++ next_token(TC_SEQSTART /* length(...) */
++ | TC_OPTERM /* length; (or newline)*/
++ | TC_GRPTERM /* length } */
++ | TC_BINOPX /* length <op> NUM */
++ | TC_COMMA /* print length, 1 */
++ );
+ rollback_token();
+ if (t_tclass & TC_SEQSTART) {
+ /* It was a "(" token. Handle just like TC_BUILTIN */
+@@ -1726,12 +1763,34 @@ static void fsrealloc(int size)
+ nfields = size;
+ }
+
++static int regexec1_nonempty(const regex_t *preg, const char *s, regmatch_t pmatch[])
++{
++ int r = regexec(preg, s, 1, pmatch, 0);
++ if (r == 0 && pmatch[0].rm_eo == 0) {
++ /* For example, happens when FS can match
++ * an empty string (awk -F ' *'). Logically,
++ * this should split into one-char fields.
++ * However, gawk 5.0.1 searches for first
++ * _non-empty_ separator string match:
++ */
++ size_t ofs = 0;
++ do {
++ ofs++;
++ if (!s[ofs])
++ return REG_NOMATCH;
++ regexec(preg, s + ofs, 1, pmatch, 0);
++ } while (pmatch[0].rm_eo == 0);
++ pmatch[0].rm_so += ofs;
++ pmatch[0].rm_eo += ofs;
++ }
++ return r;
++}
++
+ static int awk_split(const char *s, node *spl, char **slist)
+ {
+- int l, n;
++ int n;
+ char c[4];
+ char *s1;
+- regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
+
+ /* in worst case, each char would be a separate field */
+ *slist = s1 = xzalloc(strlen(s) * 2 + 3);
+@@ -1748,29 +1807,31 @@ static int awk_split(const char *s, node
+ return n; /* "": zero fields */
+ n++; /* at least one field will be there */
+ do {
++ int l;
++ regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
++
+ l = strcspn(s, c+2); /* len till next NUL or \n */
+- if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0
++ if (regexec1_nonempty(icase ? spl->r.ire : spl->l.re, s, pmatch) == 0
+ && pmatch[0].rm_so <= l
+ ) {
++ /* if (pmatch[0].rm_eo == 0) ... - impossible */
+ l = pmatch[0].rm_so;
+- if (pmatch[0].rm_eo == 0) {
+- l++;
+- pmatch[0].rm_eo++;
+- }
+ n++; /* we saw yet another delimiter */
+ } else {
+ pmatch[0].rm_eo = l;
+ if (s[l])
+ pmatch[0].rm_eo++;
+ }
+- memcpy(s1, s, l);
+- /* make sure we remove *all* of the separator chars */
+- do {
+- s1[l] = '\0';
+- } while (++l < pmatch[0].rm_eo);
+- nextword(&s1);
++ s1 = mempcpy(s1, s, l);
++ *s1++ = '\0';
+ s += pmatch[0].rm_eo;
+ } while (*s);
++
++ /* echo a-- | awk -F-- '{ print NF, length($NF), $NF }'
++ * should print "2 0 ":
++ */
++ *s1 = '\0';
++
+ return n;
+ }
+ if (c[0] == '\0') { /* null split */
+@@ -1974,7 +2035,7 @@ static int ptest(node *pattern)
+ static int awk_getline(rstream *rsm, var *v)
+ {
+ char *b;
+- regmatch_t pmatch[2];
++ regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
+ int size, a, p, pp = 0;
+ int fd, so, eo, r, rp;
+ char c, *m, *s;
--
2.17.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [OE-core] [poky][dunfell][PATCH] busybox: Fix multiple security issues in awk
2021-12-09 5:12 [poky][dunfell][PATCH] busybox: Fix multiple security issues in awk Sana Kazi
@ 2021-12-09 16:15 ` Steve Sakoman
2021-12-11 11:17 ` Ranjitsinh Rathod
0 siblings, 1 reply; 3+ messages in thread
From: Steve Sakoman @ 2021-12-09 16:15 UTC (permalink / raw)
To: sana kazi; +Cc: openembedded-core
Could you rebase this patch on current dunfell head (and fix fuzz if necessary)?
Thanks!
Steve
On Wed, Dec 8, 2021 at 7:13 PM sana kazi <sanakazisk19@gmail.com> wrote:
>
> CVE-2021-423xx-awk.patch fixes below listed CVEs for busybox:
> CVE-2021-42378, CVE-2021-42379, CVE-2021-42380, CVE-2021-42381,
> CVE-2021-42382, CVE-2021-42384, CVE-2021-42385, CVE-2021-42386
>
> Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com>
> Signed-off-by: Sana Kazi <sanakazisk19@gmail.com>
> ---
> meta/recipes-core/busybox/busybox_1.31.1.bb | 1 +
> .../busybox/files/CVE-2021-423xx-awk.patch | 203 ++++++++++++++++++
> 2 files changed, 204 insertions(+)
> create mode 100644 meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch
>
> diff --git a/meta/recipes-core/busybox/busybox_1.31.1.bb b/meta/recipes-core/busybox/busybox_1.31.1.bb
> index d9d5f4f96b..1bb0c028ac 100644
> --- a/meta/recipes-core/busybox/busybox_1.31.1.bb
> +++ b/meta/recipes-core/busybox/busybox_1.31.1.bb
> @@ -52,6 +52,7 @@ SRC_URI = "https://busybox.net/downloads/busybox-${PV}.tar.bz2;name=tarball \
> file://0001-hwclock-make-glibc-2.31-compatible.patch \
> file://0001-decompress_gunzip-Fix-DoS-if-gzip-is-corrupt.patch \
> file://0001-mktemp-add-tmpdir-option.patch \
> + file://CVE-2021-423xx-awk.patch \
> "
> SRC_URI_append_libc-musl = " file://musl.cfg "
>
> diff --git a/meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch b/meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch
> new file mode 100644
> index 0000000000..49296354cb
> --- /dev/null
> +++ b/meta/recipes-core/busybox/files/CVE-2021-423xx-awk.patch
> @@ -0,0 +1,203 @@
> +Description: fix multiple security issues in awk
> +Origin: backported awk.c from busybox 1.34.1
> +
> +CVE: CVE-2021-42378
> +CVE: CVE-2021-42379
> +CVE: CVE-2021-42380
> +CVE: CVE-2021-42381
> +CVE: CVE-2021-42382
> +CVE: CVE-2021-42384
> +CVE: CVE-2021-42385
> +CVE: CVE-2021-42386
> +
> +Upstream-Status: Backport [https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/busybox/1:1.30.1-6ubuntu3.1/busybox_1.30.1-6ubuntu3.1.debian.tar.xz]
> +
> +Comment: Refreshed first hunk and removed few hunks as they are already present in source.
> +
> +Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com>
> +
> +--- a/editors/awk.c
> ++++ b/editors/awk.c
> +@@ -272,7 +272,8 @@
> + /* if previous token class is CONCAT1 and next is CONCAT2, concatenation */
> + /* operator is inserted between them */
> + #define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM \
> +- | TC_STRING | TC_NUMBER | TC_UOPPOST)
> ++ | TC_STRING | TC_NUMBER | TC_UOPPOST \
> ++ | TC_LENGTH)
> + #define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE)
> +
> + #define OF_RES1 0x010000
> +@@ -401,7 +405,7 @@ static const char tokenlist[] ALIGN1 =
> +
> + #define OC_B OC_BUILTINCVE-2021-423xx-awk.patch
> +
> +-static const uint32_t tokeninfo[] = {
> ++static const uint32_t tokeninfo[] ALIGN4 = {
> + 0,
> + 0,
> + OC_REGEXP
> +@@ -1067,8 +1071,10 @@ static uint32_t next_token(uint32_t expe
> + const uint32_t *ti;
> +
> + if (t_rollback) {
> ++ debug_printf_parse("%s: using rolled-back token\n", __func__);
> + t_rollback = FALSE;
> + } else if (concat_inserted) {
> ++ debug_printf_parse("%s: using concat-inserted token\n", __func__);
> + concat_inserted = FALSE;
> + t_tclass = save_tclass;
> + t_info = save_info;
> +@@ -1197,7 +1203,11 @@ static uint32_t next_token(uint32_t expe
> + goto readnext;
> +
> + /* insert concatenation operator when needed */
> +- if ((ltclass & TC_CONCAT1) && (tc & TC_CONCAT2) && (expected & TC_BINOP)) {
> ++ debug_printf_parse("%s: %x %x %x concat_inserted?\n", __func__,
> ++ (ltclass & TC_CONCAT1), (tc & TC_CONCAT2), (expected & TC_BINOP));
> ++ if ((ltclass & TC_CONCAT1) && (tc & TC_CONCAT2) && (expected & TC_BINOP)
> ++ && !(ltclass == TC_LENGTH && tc == TC_SEQSTART) /* but not for "length(..." */
> ++ ) {
> + concat_inserted = TRUE;
> + save_tclass = tc;
> + save_info = t_info;
> +@@ -1205,6 +1215,7 @@ static uint32_t next_token(uint32_t expe
> + t_info = OC_CONCAT | SS | P(35);
> + }
> +
> ++ debug_printf_parse("%s: t_tclass=tc=%x\n", __func__, t_tclass);
> + t_tclass = tc;
> + }
> + ltclass = t_tclass;
> +@@ -1215,6 +1226,7 @@ static uint32_t next_token(uint32_t expe
> + EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
> + }
> +
> ++ debug_printf_parse("%s: returning, ltclass:%x t_double:%f\n", __func__, ltclass, t_double);
> + return ltclass;
> + #undef concat_inserted
> + #undef save_tclass
> +@@ -1279,7 +1291,7 @@ static node *parse_expr(uint32_t iexp)
> + glptr = NULL;
> +
> + } else if (tc & (TC_BINOP | TC_UOPPOST)) {
> +- debug_printf_parse("%s: TC_BINOP | TC_UOPPOST\n", __func__);
> ++ debug_printf_parse("%s: TC_BINOP | TC_UOPPOST tc:%x\n", __func__, tc);
> + /* for binary and postfix-unary operators, jump back over
> + * previous operators with higher priority */
> + vn = cn;
> +@@ -1346,8 +1359,10 @@ static node *parse_expr(uint32_t iexp)
> + v = cn->l.v = xzalloc(sizeof(var));
> + if (tc & TC_NUMBER)
> + setvar_i(v, t_double);
> +- else
> ++ else {
> + setvar_s(v, t_string);
> ++ xtc &= ~TC_UOPPOST; /* "str"++ is not allowed */
> ++ }
> + break;
> +
> + case TC_REGEXP:
> +@@ -1383,7 +1398,12 @@ static node *parse_expr(uint32_t iexp)
> +
> + case TC_LENGTH:
> + debug_printf_parse("%s: TC_LENGTH\n", __func__);
> +- next_token(TC_SEQSTART | TC_OPTERM | TC_GRPTERM);
> ++ next_token(TC_SEQSTART /* length(...) */
> ++ | TC_OPTERM /* length; (or newline)*/
> ++ | TC_GRPTERM /* length } */
> ++ | TC_BINOPX /* length <op> NUM */
> ++ | TC_COMMA /* print length, 1 */
> ++ );
> + rollback_token();
> + if (t_tclass & TC_SEQSTART) {
> + /* It was a "(" token. Handle just like TC_BUILTIN */
> +@@ -1726,12 +1763,34 @@ static void fsrealloc(int size)
> + nfields = size;
> + }
> +
> ++static int regexec1_nonempty(const regex_t *preg, const char *s, regmatch_t pmatch[])
> ++{
> ++ int r = regexec(preg, s, 1, pmatch, 0);
> ++ if (r == 0 && pmatch[0].rm_eo == 0) {
> ++ /* For example, happens when FS can match
> ++ * an empty string (awk -F ' *'). Logically,
> ++ * this should split into one-char fields.
> ++ * However, gawk 5.0.1 searches for first
> ++ * _non-empty_ separator string match:
> ++ */
> ++ size_t ofs = 0;
> ++ do {
> ++ ofs++;
> ++ if (!s[ofs])
> ++ return REG_NOMATCH;
> ++ regexec(preg, s + ofs, 1, pmatch, 0);
> ++ } while (pmatch[0].rm_eo == 0);
> ++ pmatch[0].rm_so += ofs;
> ++ pmatch[0].rm_eo += ofs;
> ++ }
> ++ return r;
> ++}
> ++
> + static int awk_split(const char *s, node *spl, char **slist)
> + {
> +- int l, n;
> ++ int n;
> + char c[4];
> + char *s1;
> +- regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
> +
> + /* in worst case, each char would be a separate field */
> + *slist = s1 = xzalloc(strlen(s) * 2 + 3);
> +@@ -1748,29 +1807,31 @@ static int awk_split(const char *s, node
> + return n; /* "": zero fields */
> + n++; /* at least one field will be there */
> + do {
> ++ int l;
> ++ regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
> ++
> + l = strcspn(s, c+2); /* len till next NUL or \n */
> +- if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0
> ++ if (regexec1_nonempty(icase ? spl->r.ire : spl->l.re, s, pmatch) == 0
> + && pmatch[0].rm_so <= l
> + ) {
> ++ /* if (pmatch[0].rm_eo == 0) ... - impossible */
> + l = pmatch[0].rm_so;
> +- if (pmatch[0].rm_eo == 0) {
> +- l++;
> +- pmatch[0].rm_eo++;
> +- }
> + n++; /* we saw yet another delimiter */
> + } else {
> + pmatch[0].rm_eo = l;
> + if (s[l])
> + pmatch[0].rm_eo++;
> + }
> +- memcpy(s1, s, l);
> +- /* make sure we remove *all* of the separator chars */
> +- do {
> +- s1[l] = '\0';
> +- } while (++l < pmatch[0].rm_eo);
> +- nextword(&s1);
> ++ s1 = mempcpy(s1, s, l);
> ++ *s1++ = '\0';
> + s += pmatch[0].rm_eo;
> + } while (*s);
> ++
> ++ /* echo a-- | awk -F-- '{ print NF, length($NF), $NF }'
> ++ * should print "2 0 ":
> ++ */
> ++ *s1 = '\0';
> ++
> + return n;
> + }
> + if (c[0] == '\0') { /* null split */
> +@@ -1974,7 +2035,7 @@ static int ptest(node *pattern)
> + static int awk_getline(rstream *rsm, var *v)
> + {
> + char *b;
> +- regmatch_t pmatch[2];
> ++ regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
> + int size, a, p, pp = 0;
> + int fd, so, eo, r, rp;
> + char c, *m, *s;
> --
> 2.17.1
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#159440): https://lists.openembedded.org/g/openembedded-core/message/159440
> Mute This Topic: https://lists.openembedded.org/mt/87606868/3620601
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [steve@sakoman.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [poky][dunfell][PATCH] busybox: Fix multiple security issues in awk
2021-12-09 16:15 ` [OE-core] " Steve Sakoman
@ 2021-12-11 11:17 ` Ranjitsinh Rathod
0 siblings, 0 replies; 3+ messages in thread
From: Ranjitsinh Rathod @ 2021-12-11 11:17 UTC (permalink / raw)
To: openembedded-core
[-- Attachment #1: Type: text/plain, Size: 126 bytes --]
Hi Steve,
Just sent a v2 patch for this after rebasing and removing the patch fuzz warning.
Thanks,
Ranjitsinh Rathod
[-- Attachment #2: Type: text/html, Size: 146 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2021-12-11 11:17 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-09 5:12 [poky][dunfell][PATCH] busybox: Fix multiple security issues in awk Sana Kazi
2021-12-09 16:15 ` [OE-core] " Steve Sakoman
2021-12-11 11:17 ` Ranjitsinh Rathod
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.