Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2231750/?format=api
{ "id": 2231750, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2231750/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20260501112421.3373616-2-avinal.xlvii@gmail.com/", "project": { "id": 41, "url": "http://patchwork.ozlabs.org/api/1.1/projects/41/?format=api", "name": "GNU C Library", "link_name": "glibc", "list_id": "libc-alpha.sourceware.org", "list_email": "libc-alpha@sourceware.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260501112421.3373616-2-avinal.xlvii@gmail.com>", "date": "2026-05-01T11:24:04", "name": "[v2,1/2] intl: Import plural expression hardening from GNU gettext", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "163fa8dfe860eb34f8d97497da7e194801f8581d", "submitter": { "id": 88549, "url": "http://patchwork.ozlabs.org/api/1.1/people/88549/?format=api", "name": "Avinal Kumar", "email": "avinal.xlvii@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20260501112421.3373616-2-avinal.xlvii@gmail.com/mbox/", "series": [ { "id": 502445, "url": "http://patchwork.ozlabs.org/api/1.1/series/502445/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=502445", "date": "2026-05-01T11:24:03", "name": "intl: Import plural expression hardening from GNU gettext", "version": 2, "mbox": "http://patchwork.ozlabs.org/series/502445/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2231750/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2231750/checks/", "tags": {}, "headers": { "Return-Path": "<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "libc-alpha@sourceware.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "libc-alpha@sourceware.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=mQcZcKPQ;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=38.145.34.32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=mQcZcKPQ", "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com", "sourceware.org; spf=pass smtp.mailfrom=gmail.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=2607:f8b0:4864:20::102b" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g6TH03bWpz1xqf\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 01 May 2026 21:25:16 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 5D9994099F94\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 1 May 2026 11:25:14 +0000 (GMT)", "from mail-pj1-x102b.google.com (mail-pj1-x102b.google.com\n [IPv6:2607:f8b0:4864:20::102b])\n by sourceware.org (Postfix) with ESMTPS id 0E8AE43B5507\n for <libc-alpha@sourceware.org>; Fri, 1 May 2026 11:24:53 +0000 (GMT)", "by mail-pj1-x102b.google.com with SMTP id\n 98e67ed59e1d1-358e3cc5e7eso1178866a91.0\n for <libc-alpha@sourceware.org>; Fri, 01 May 2026 04:24:52 -0700 (PDT)", "from fedoraemon.neon-universe.ts.net\n ([2406:7400:11d:a9dc:9a14:72eb:1a60:868a])\n by smtp.gmail.com with ESMTPSA id\n 98e67ed59e1d1-364be028cd6sm5198006a91.14.2026.05.01.04.24.48\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 01 May 2026 04:24:49 -0700 (PDT)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org 5D9994099F94", "OpenDKIM Filter v2.11.0 sourceware.org 0E8AE43B5507" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 0E8AE43B5507", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 0E8AE43B5507", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777634693; cv=none;\n b=V49Hkw8fekxzqnW5eUmZmNiuA3yHvqBGODXITSwF4xFX8ApRWl5+Te36uXOOEDBCoeIL5F1Bz9nGajbHc3nUoi8k/YMLzL11THT4eNyuTJLk+a0JUd10A5PaJNR4mg09pcbr+2rS0IN+rULa80THGEuPLUvac7vd5hm9jy8fryc=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777634693; c=relaxed/simple;\n bh=/hSjtZ6i6MAv+jmhypRmyj30V6+zGvaSJpOYs+lSM94=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=CTFbv5EfTG7FsYuvWiOQMZIxhaq2yeqg3LCwrx4E4ZFq7t8iOsMveCnGYZwWetPMsiD2IyRYU06P55ZhOODkt1nwvlLzZNNxhFTdRqFYEsYafrvl1XMwLSY7VrzFWtXqS2oVOQrFiIIMPX1Pq1aZPWvF1tlfc5tTWHzUe/YxvSk=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1777634691; x=1778239491; darn=sourceware.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=uLt4CFy/3NlSbCcChSX+a//cu6dqsbc1DJ5Whj1NgGA=;\n b=mQcZcKPQaVu29DoWk6sCBYFs6XCUhDkwikt4q8fG/EBxLKvarocXxNKOmS5SAqCLeH\n k2CXqPpdUc9P5ehk1EHqaSxXFefPGnXo31k5IFDFkzflXkFQHRPtMJVviX8i9D79LbyN\n dfUWUMJrRR/RitJ+SVIhfX7AX8NpSKDpcj98zO6OPsXSYcKn2gdVIkoJ/hHZ8vK+ubsP\n q8U8jQLHSYromEP2xi0gq5PwsRPv1s+DbN9JKJAENgu+6ZmpkX2MpVk9G8G3BI0myIYP\n U+O3bs8AXR8byAtkLJ6FuZLQuEpuFfUKKSLnF86pB37tponuUKJln7iXeVK0pVCoUjsq\n EmUg==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777634691; x=1778239491;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=uLt4CFy/3NlSbCcChSX+a//cu6dqsbc1DJ5Whj1NgGA=;\n b=DsiVNgklBj1OaEwZKAham+j4csXf0rPayJN84CBvmqLvwksPROxlvayxz/tpNPptdN\n To1JS1lDEKpwxxK/PMJWRWlTDiXLIitP1JcZe2KYOLwdtaK5VgtYKbMsXkBAIE5LdyQr\n M8kJ0Coxlb7A7TDqVo0LMk/Q6jcs2ePXkBdnnjG7+KlAg6yDh3fksSFkQbOzyOWReUkl\n k/Spf/Co9smdPpU+8aR0a2PkrgDe5FsJdT3VVP6fqtfM1YqNoD1486fxda91Hc0/8Dm3\n Z8YsNv5YfMa/MyK1jWV75Rstm1Iu82SEy/96RdvO3J2zaDFKgqqkV+9M7o1+z6PyhOA7\n +Zrg==", "X-Gm-Message-State": "AOJu0YzIdoc7mo/3amoun8hTfgX+zppDP4V/DJ8wSdiZ4DTb3bRVtqVJ\n B83lcztutY/S79Rfw5NvstLGz063sIECwquHEGs9KjhksSIe10DVuxDNLtlQZg==", "X-Gm-Gg": "AeBDiesDnB/pKR1SXKss3+9Qb4Dahw7pJ2CDGaHgQEpz9yKa8PfC396vJS1DZar9F54\n DpRHln3qoMyic3n5rWYbQYL9gkgL8xMgsp6YB9GrWZ+sGiILzUxw8UZHZIfQJOU9+CE/GEaLhti\n EBPwflKyBON9u4PLPbDRRXOeGPYG+z+16EXGUD0kzz/Zex/ila+Q/MFagDUNoVdglFNeDVvCHpq\n ZK/PZc8xorb8smYLIakPdx/MMXZLnz55ydAQcVyi7rJSjp9jEFC5PdqBJ/N8TAXthgHRfi6Shki\n Z5H/O/Ib2d+J0VOH2FwSyZY0G3NutiP2znq1w3KkKEHVThBo5FWwNz3FXIN2pvgLhwDIPnahcBh\n WVgnA59nsQp+3BMx7Kf0cTwlllJWXKKuXYfPX5a5bNLMwRlWAKQ/Rsjoq8Q1QR9FaJP7umTqvHy\n SuUWVO7/r43YIwoJ6que8glhAoVwo/bJ2m9rr2NN1bBcIQwZL25Eg5rr2ygUkb0m7NdQ==", "X-Received": "by 2002:a17:90b:4b8f:b0:35f:c729:de9f with SMTP id\n 98e67ed59e1d1-364c32cdbcdmr7516315a91.27.1777634690865;\n Fri, 01 May 2026 04:24:50 -0700 (PDT)", "From": "Avinal Kumar <avinal.xlvii@gmail.com>", "To": "libc-alpha@sourceware.org", "Cc": "adhemerval.zanella@linaro.org", "Subject": "[PATCH v2 1/2] intl: Import plural expression hardening from GNU\n gettext", "Date": "Fri, 1 May 2026 16:54:04 +0530", "Message-ID": "<20260501112421.3373616-2-avinal.xlvii@gmail.com>", "X-Mailer": "git-send-email 2.54.0", "In-Reply-To": "<20260501112421.3373616-1-avinal.xlvii@gmail.com>", "References": "<20260501112421.3373616-1-avinal.xlvii@gmail.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libc-alpha@sourceware.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Libc-alpha mailing list <libc-alpha.sourceware.org>", "List-Unsubscribe": "<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>", "List-Archive": "<https://sourceware.org/pipermail/libc-alpha/>", "List-Post": "<mailto:libc-alpha@sourceware.org>", "List-Help": "<mailto:libc-alpha-request@sourceware.org?subject=help>", "List-Subscribe": "<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>", "Errors-To": "libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org" }, "content": "The plural expression evaluator plural_eval() in eval-plural.h uses\nunbounded recursion, which can cause a stack overflow crash with\ndeeply nested expressions in malicious .mo files. This is\nparticularly dangerous on threads with small stacks (musl libc\ndefault: 128 KB, AIX 7 default: 96 KB, glibc after ulimit -s 260:\n~3919 recursions max).\n\nAdditionally, division by zero in plural expressions triggers\nraise(SIGFPE), which is not multithread-safe, catching SIGFPE\nrequires per-process signal handlers that race with other threads.\n\nFix both by importing the hardening from GNU gettext:\n\n- Replace unbounded plural_eval() with depth-limited\n plural_eval_recurse() (EVAL_MAXDEPTH=100), returning a\n struct eval_result with status instead of a bare unsigned long.\n\n- Return PE_INTDIV status on division by zero instead of raising\n SIGFPE. Remove the architecture-specific INTDIV0_RAISES_SIGFPE\n macro and the conditional #include <signal.h>.\n\n- Update plural_lookup() in dcigettext.c to handle the new return\n type, falling back to index 0 on any evaluation failure.\n\nBased on GNU gettext commits ef37a1540 and 726bfb1d1.\nOriginal author: Bruno Haible <bruno@clisp.org>\nDiscussed on: https://sourceware.org/pipermail/libc-alpha/2023-October/152010.html\n\nSigned-off-by: Avinal Kumar <avinal.xlvii@gmail.com>\n---\n intl/dcigettext.c | 27 +++------\n intl/eval-plural.h | 139 ++++++++++++++++++++++++++++++++++-----------\n intl/plural-exp.h | 21 ++++++-\n 3 files changed, 134 insertions(+), 53 deletions(-)", "diff": "diff --git a/intl/dcigettext.c b/intl/dcigettext.c\nindex 9ebc54eeec..fb948c1092 100644\n--- a/intl/dcigettext.c\n+++ b/intl/dcigettext.c\n@@ -68,20 +68,6 @@ extern int errno;\n \n #include <locale.h>\n \n-#ifdef _LIBC\n- /* Guess whether integer division by zero raises signal SIGFPE.\n- Set to 1 only if you know for sure. In case of doubt, set to 0. */\n-# if defined __alpha__ || defined __arm__ || defined __i386__ \\\n- || defined __m68k__ || defined __s390x__\n-# define INTDIV0_RAISES_SIGFPE 1\n-# else\n-# define INTDIV0_RAISES_SIGFPE 0\n-# endif\n-#endif\n-#if !INTDIV0_RAISES_SIGFPE\n-# include <signal.h>\n-#endif\n-\n #if defined HAVE_SYS_PARAM_H || defined _LIBC\n # include <sys/param.h>\n #endif\n@@ -1381,14 +1367,19 @@ plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,\n \t const char *translation, size_t translation_len)\n {\n struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;\n+ struct eval_result result;\n unsigned long int index;\n const char *p;\n \n- index = plural_eval (domaindata->plural, n);\n- if (index >= domaindata->nplurals)\n- /* This should never happen. It means the plural expression and the\n- given maximum value do not match. */\n+ result = plural_eval (domaindata->plural, n);\n+ if (result.status != PE_OK)\n+ /* The plural expression evaluation failed. */\n index = 0;\n+ else if (result.value >= domaindata->nplurals)\n+ /* The plural expression and the given maximum value do not match. */\n+ index = 0;\n+ else\n+ index = result.value;\n \n /* Skip INDEX strings at TRANSLATION. */\n p = translation;\ndiff --git a/intl/eval-plural.h b/intl/eval-plural.h\nindex 243f6027a1..e231e9bfd3 100644\n--- a/intl/eval-plural.h\n+++ b/intl/eval-plural.h\n@@ -14,24 +14,59 @@\n You should have received a copy of the GNU Lesser General Public License\n along with this program. If not, see <https://www.gnu.org/licenses/>. */\n \n+/* Written by Ulrich Drepper and Bruno Haible. */\n+\n #ifndef STATIC\n #define STATIC static\n #endif\n \n-/* Evaluate the plural expression and return an index value. */\n-STATIC\n-unsigned long int\n-plural_eval (const struct expression *pexp, unsigned long int n)\n+/* While the bison parser is able to support expressions of a maximum depth\n+ of YYMAXDEPTH = 10000, the runtime evaluation of a parsed plural expression\n+ has a smaller maximum recursion depth.\n+ If we did not limit the recursion depth, a program that just invokes\n+ ngettext() on a thread other than the main thread could get a crash by\n+ stack overflow in the following circumstances:\n+ - On systems with glibc, after the stack size has been reduced,\n+ e.g. on x86_64 systems after \"ulimit -s 260\".\n+ This stack size is only sufficient for ca. 3919 recursions.\n+ Cf. <https://unix.stackexchange.com/questions/620720/>\n+ - On systems with musl libc, because there the thread stack size (for a\n+ thread other than the main thread) by default is only 128 KB, see\n+ <https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size>.\n+ - On AIX 7 systems, because there the thread stack size (for a thread\n+ other than the main thread) by default is only 96 KB, see\n+ <https://www.ibm.com/docs/en/aix/7.1?topic=programming-threads-library-options>.\n+ This stack size is only sufficient for between 887 and 1363 recursions,\n+ depending on the compiler and compiler optimization options.\n+ A maximum depth of 100 is a large enough for all practical needs\n+ and also small enough to avoid stack overflow even with small thread stack\n+ sizes. */\n+#ifndef EVAL_MAXDEPTH\n+# define EVAL_MAXDEPTH 100\n+#endif\n+\n+/* A shorthand that denotes a successful evaluation result with a value V. */\n+#define OK(v) (struct eval_result) { .status = PE_OK, .value = (v) }\n+\n+/* Evaluates a plural expression PEXP for n=N, up to ALLOWED_DEPTH. */\n+static struct eval_result\n+plural_eval_recurse (const struct expression *pexp, unsigned long int n,\n+\t\t unsigned int allowed_depth)\n {\n+ if (allowed_depth == 0)\n+ /* The allowed recursion depth is exhausted. */\n+ return (struct eval_result) { .status = PE_STACKOVF };\n+ allowed_depth--;\n+\n switch (pexp->nargs)\n {\n case 0:\n switch (pexp->operation)\n \t{\n \tcase var:\n-\t return n;\n+\t return OK (n);\n \tcase num:\n-\t return pexp->val.num;\n+\t return OK (pexp->val.num);\n \tdefault:\n \t break;\n \t}\n@@ -40,52 +75,73 @@ plural_eval (const struct expression *pexp, unsigned long int n)\n case 1:\n {\n \t/* pexp->operation must be lnot. */\n-\tunsigned long int arg = plural_eval (pexp->val.args[0], n);\n-\treturn ! arg;\n+\tstruct eval_result arg =\n+\t plural_eval_recurse (pexp->val.args[0], n, allowed_depth);\n+\tif (arg.status != PE_OK)\n+\t return arg;\n+\treturn OK (! arg.value);\n }\n case 2:\n {\n-\tunsigned long int leftarg = plural_eval (pexp->val.args[0], n);\n+\tstruct eval_result leftarg =\n+\t plural_eval_recurse (pexp->val.args[0], n, allowed_depth);\n+\tif (leftarg.status != PE_OK)\n+\t return leftarg;\n \tif (pexp->operation == lor)\n-\t return leftarg || plural_eval (pexp->val.args[1], n);\n+\t {\n+\t if (leftarg.value)\n+\t return OK (1);\n+\t struct eval_result rightarg =\n+\t plural_eval_recurse (pexp->val.args[1], n, allowed_depth);\n+\t if (rightarg.status != PE_OK)\n+\t return rightarg;\n+\t return OK (rightarg.value ? 1 : 0);\n+\t }\n \telse if (pexp->operation == land)\n-\t return leftarg && plural_eval (pexp->val.args[1], n);\n+\t {\n+\t if (!leftarg.value)\n+\t return OK (0);\n+\t struct eval_result rightarg =\n+\t plural_eval_recurse (pexp->val.args[1], n, allowed_depth);\n+\t if (rightarg.status != PE_OK)\n+\t return rightarg;\n+\t return OK (rightarg.value ? 1 : 0);\n+\t }\n \telse\n \t {\n-\t unsigned long int rightarg = plural_eval (pexp->val.args[1], n);\n+\t struct eval_result rightarg =\n+\t plural_eval_recurse (pexp->val.args[1], n, allowed_depth);\n+\t if (rightarg.status != PE_OK)\n+\t return rightarg;\n \n \t switch (pexp->operation)\n \t {\n \t case mult:\n-\t\treturn leftarg * rightarg;\n+\t\treturn OK (leftarg.value * rightarg.value);\n \t case divide:\n-#if !INTDIV0_RAISES_SIGFPE\n-\t\tif (rightarg == 0)\n-\t\t raise (SIGFPE);\n-#endif\n-\t\treturn leftarg / rightarg;\n+\t\tif (rightarg.value == 0)\n+\t\t return (struct eval_result) { .status = PE_INTDIV };\n+\t\treturn OK (leftarg.value / rightarg.value);\n \t case module:\n-#if !INTDIV0_RAISES_SIGFPE\n-\t\tif (rightarg == 0)\n-\t\t raise (SIGFPE);\n-#endif\n-\t\treturn leftarg % rightarg;\n+\t\tif (rightarg.value == 0)\n+\t\t return (struct eval_result) { .status = PE_INTDIV };\n+\t\treturn OK (leftarg.value % rightarg.value);\n \t case plus:\n-\t\treturn leftarg + rightarg;\n+\t\treturn OK (leftarg.value + rightarg.value);\n \t case minus:\n-\t\treturn leftarg - rightarg;\n+\t\treturn OK (leftarg.value - rightarg.value);\n \t case less_than:\n-\t\treturn leftarg < rightarg;\n+\t\treturn OK (leftarg.value < rightarg.value);\n \t case greater_than:\n-\t\treturn leftarg > rightarg;\n+\t\treturn OK (leftarg.value > rightarg.value);\n \t case less_or_equal:\n-\t\treturn leftarg <= rightarg;\n+\t\treturn OK (leftarg.value <= rightarg.value);\n \t case greater_or_equal:\n-\t\treturn leftarg >= rightarg;\n+\t\treturn OK (leftarg.value >= rightarg.value);\n \t case equal:\n-\t\treturn leftarg == rightarg;\n+\t\treturn OK (leftarg.value == rightarg.value);\n \t case not_equal:\n-\t\treturn leftarg != rightarg;\n+\t\treturn OK (leftarg.value != rightarg.value);\n \t default:\n \t\tbreak;\n \t }\n@@ -96,10 +152,25 @@ plural_eval (const struct expression *pexp, unsigned long int n)\n case 3:\n {\n \t/* pexp->operation must be qmop. */\n-\tunsigned long int boolarg = plural_eval (pexp->val.args[0], n);\n-\treturn plural_eval (pexp->val.args[boolarg ? 1 : 2], n);\n+\tstruct eval_result boolarg =\n+\t plural_eval_recurse (pexp->val.args[0], n, allowed_depth);\n+\tif (boolarg.status != PE_OK)\n+\t return boolarg;\n+\treturn plural_eval_recurse (pexp->val.args[boolarg.value ? 1 : 2], n,\n+\t\t\t\t allowed_depth);\n }\n }\n /* NOTREACHED */\n- return 0;\n+ return (struct eval_result) { .status = PE_ASSERT };\n+}\n+\n+/* Evaluates a plural expression PEXP for n=N. */\n+STATIC\n+struct eval_result\n+plural_eval (const struct expression *pexp, unsigned long int n)\n+{\n+ return plural_eval_recurse (pexp, n, EVAL_MAXDEPTH);\n }\n+\n+#undef OK\n+\ndiff --git a/intl/plural-exp.h b/intl/plural-exp.h\nindex f0d3a611e1..e2e0c30563 100644\n--- a/intl/plural-exp.h\n+++ b/intl/plural-exp.h\n@@ -26,6 +26,7 @@\n extern \"C\" {\n #endif\n \n+/* Parsing a plural expression. */\n \n enum expression_operator\n {\n@@ -109,8 +110,26 @@ extern void EXTRACT_PLURAL_EXPRESSION (const char *nullentry,\n \t\t\t\t unsigned long int *npluralsp)\n attribute_hidden;\n \n+/* Evaluating a parsed plural expression. */\n+\n+enum eval_status\n+{\n+ PE_OK, /* Evaluation succeeded, produced a value */\n+ PE_INTDIV, /* Integer division by zero */\n+ PE_INTOVF, /* Integer overflow */\n+ PE_STACKOVF, /* Stack overflow */\n+ PE_ASSERT /* Assertion failure */\n+};\n+\n+struct eval_result\n+{\n+ enum eval_status status;\n+ unsigned long int value; /* Only relevant for status == PE_OK */\n+};\n+\n+\n #if !defined (_LIBC) && !defined (IN_LIBINTL) && !defined (IN_LIBGLOCALE)\n-extern unsigned long int plural_eval (const struct expression *pexp,\n+extern struct eval_result plural_eval (const struct expression *pexp,\n \t\t\t\t unsigned long int n);\n #endif\n \n", "prefixes": [ "v2", "1/2" ] }