From patchwork Tue Sep 1 13:39:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Avinal Kumar X-Patchwork-Id: 142633 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D32F04BA23E4 for ; Tue, 1 Sep 2026 13:39:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D32F04BA23E4 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=DUmAQyPB X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pf1-x42e.google.com (mail-pf1-x42e.google.com [IPv6:2607:f8b0:4864:20::42e]) by sourceware.org (Postfix) with ESMTPS id 2B6AF4BA79B7 for ; Tue, 1 Sep 2026 13:39:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2B6AF4BA79B7 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2B6AF4BA79B7 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::42e ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788269958; cv=none; b=n0Ts+g8mhPKMtCrDc1ZXVcgYuk6MBdVRfjX/Flt4HM/+VrD5XQmeZEDAN7dcoifPE5q+wgjYZNR0lxihAktBcrlXI1Pe9AeRbKkD7WiUBDi+44lTcxMsCA1TJxAxDh/N7N8WEinis75UTeBwAwLTW7CwyJPjq80UcZx4TWBJpFg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788269958; c=relaxed/simple; bh=wazduBchIWkKHRB7n5Wr7BojGPhqxLlRwCZECq0SUMo=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=G8VyHJjINjKvShANi1CSaY7mzg/hzAsLh+lD1GkmHc12wx3Elh0HnpMPxXGNR12V0GJE26glZ9QRveZzBTt23tWrXO6yqNQcth3dUuxrNLoWfwL6VxwmePBV9DiIH4hLaba3L3hrpTtSD6E0NP3bGHcD0/FWuu+rIXOw2HzzGcU= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=DUmAQyPB DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2B6AF4BA79B7 Received: by mail-pf1-x42e.google.com with SMTP id d2e1a72fcca58-84e84a6c4bfso1102073b3a.1 for ; Tue, 01 Sep 2026 06:39:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788269957; x=1788874757; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=MOp/Jg20DlHHxJ11jeSLqtRXSmtEghNXbVrGIx7JCVA=; b=DUmAQyPBQZI+dQFpsfAIPFawJMajKtIOZAl8Sbfk7s4OX0AmTUYRCrNwxfTXhn2LmI yDKdEKjTQ3a/u4Ex+xMjuuaaoKk5ZjdMxB2yk25UIowsR6XFI2s3sIbNTcTi/oXgiLll ycalLEGsr2VVXcbH2NuhYgLNycCPTwnUjFEs1irZ+rawhTL2BdhxZSm8wmOfN4eODC3L qEV5YUH41V2Ilc7lsHB+3XW4aovaIjPBYHdxepJHEs+xl9E/PlbVZV5uSf0IAXmDmEBe 9uYgdlQaKrDsKUOm3o5AliKDfYF/hCsc4yOGep3bGUeBDF2oS5YDc3P2we8HUGnvcVwr Q1Aw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788269957; x=1788874757; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=MOp/Jg20DlHHxJ11jeSLqtRXSmtEghNXbVrGIx7JCVA=; b=Vu5hmtXJdrSS+BHQMKGjrwD8A9yzAyeByrd7zRxUfiUtEP5Bn7BwEzbNuub9yWTCnJ 0B5GBnWEFIQCMwR0WlyjHUI6e+mLlqCt1IT0XP1NjIouEsL4+LINg+h3OOUTmCmRwBkV z2esrfE9y/hDU5h2n163KRzmpoCyX+QIv15RDHWGEDcSdX6CDVT+n7sU2NuV8dmWJ1Th hL0mcmAJqE78ekFYQ4uomAPeTVExThxM0BYD4H3YITq1FkjTNwdsjri8GbUbXtjkMBn1 RHGKFc2zNY+w+svgtQ4On04DI8bo5qMMk7EMNh5D38vhzXbLA+rX/yjLsdw/4x6aB0qe tZjg== X-Gm-Message-State: AFuF++kpkVuvVWSlaZBS9CvMceA2GsTi0t4nOnPyEcR5OKy7R85Qas9p 2MXFkiwSf6qQt69682/gN0W/kEeBargsYY4CJYOVdKCfkv7mV3EwonmjuzcUjQ== X-Gm-Gg: AR+sD11ni0WEWkdwBdtBzJ8ihZUWPj8Gw5owcdeMjVc5AjV5fuvfj8jn8uthA6xrtVd nlBivK+YGE/SFoPh83zAp0wgqwav1ZwoIPneZuca8WR9n1W4Bq/o+yzv5WtE6izZzYSotlRJ5nY pbdZ28BtDoOeIJv6rpI56JQWasuTztmDvra/UalNt0GIp7aoYsMxZrWcXCMPi9ZcSHw+nrslDtg nk9jt9yl36lZKrtMfsZFHiglXMdlobUmxS5Lrt2wWM3I45QpaF4bsUNqeM3kDXzUOA9Do412jXf QZHyrOF3xPXAJZQhyZfAx5oSRRhZYee1f+XCRbM64r3T0uZn3W5+rEHmFn2PEK81E/OBbnRLXBl V1AJOCAeECpYG/oxODr9Ck6uFe3OHe4l7u9p9cirlr7Qrex7DzH6JYH9/iegc9+MEB6hr3gbAvR yoD6A+l+eatWXY39GT1+4QiZLhZ3ScJHy2RCsHOv6QxLbHyA9r5oV6IDp8wtT6n/yLF8mskC1eJ 7P53BVR6gn7DNN4MU/1 X-Received: by 2002:a05:6a00:7099:b0:853:af4d:6292 with SMTP id d2e1a72fcca58-85bc6173f11mr4849696b3a.0.1788269956958; Tue, 01 Sep 2026 06:39:16 -0700 (PDT) Received: from fedoraemon.neon-universe.ts.net ([2406:7400:11d:1fd2:eec2:ab88:e4ff:cced]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-85be8e5e5fesm1133175b3a.29.2026.09.01.06.39.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Sep 2026 06:39:16 -0700 (PDT) From: Avinal Kumar To: libc-alpha@sourceware.org Cc: Avinal Kumar , Adhemerval Zanella Subject: [PATCH v3] intl: Restrict path traversal when using LANGUAGE env var [BZ #17142, CVE-2026-84243] Date: Tue, 1 Sep 2026 19:09:02 +0530 Message-ID: <20260901133902.184341-1-avinal.xlvii@gmail.com> X-Mailer: git-send-email 2.55.0 MIME-Version: 1.0 X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_PBL, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org The fix for CVE-2014-0475 (Bug 17137) added valid_locale_name() in locale/findlocale.c to reject locale names containing ".." path components. However, the LANGUAGE environment variable processing in intl/dcigettext.c was not covered by that fix. An attacker who can set LANGUAGE (e.g. via SSH AcceptEnv) can force any gettext-using program to load a crafted .mo file from an arbitrary filesystem location via directory traversal. Remove the ENABLE_SECURE gate from the IS_PATH_WITH_DIR check so it applies to all binaries, not just SUID/SGID ones, and extend it to also reject the bare ".." entry which contains no directory separator but still names the parent directory. Add tst-gettext-path-traversal to verify that locale names containing path separators or ".." are rejected by the LANGUAGE processing loop. This fixes bug 17142 and CVE-2026-84243. Suggested-by: Adhemerval Zanella Signed-off-by: Avinal Kumar --- Changes from v3: - added CVE details - moved to libc-alpha intl/Makefile | 4 ++ intl/dcigettext.c | 8 +-- intl/tst-gettext-path-traversal.c | 83 +++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 intl/tst-gettext-path-traversal.c diff --git a/intl/Makefile b/intl/Makefile index a8b41a1993..869b2eeebe 100644 --- a/intl/Makefile +++ b/intl/Makefile @@ -43,6 +43,7 @@ endif tests = \ tst-gettext-c-utf8 \ tst-ngettext \ + tst-gettext-path-traversal \ # tests before-compile += $(objpfx)msgs.h @@ -125,6 +126,7 @@ $(objpfx)tst-plural-eval.out: tst-plural-eval.sh $(objpfx)tst-plural-eval $(objpfx)tst-codeset.out: $(codeset_mo) $(objpfx)tst-gettext3.out: $(codeset_mo) $(objpfx)tst-gettext5.out: $(codeset_mo) +$(objpfx)tst-gettext-path-traversal.out: $(codeset_mo) endif LOCALES := de_DE.ISO-8859-1 de_DE.UTF-8 en_US.ANSI_X3.4-1968 fr_FR.ISO-8859-1 \ @@ -139,6 +141,7 @@ $(objpfx)tst-gettext4.out: $(gen-locales) $(objpfx)tst-gettext5.out: $(gen-locales) $(objpfx)tst-gettext6.out: $(gen-locales) $(objpfx)tst-gettext-c-utf8.out: $(gen-locales) +$(objpfx)tst-gettext-path-traversal.out: $(gen-locales) $(objpfx)tst-translit.out: $(gen-locales) endif @@ -159,6 +162,7 @@ CFLAGS-tst-gettext4.c += -DOBJPFX=\"$(objpfx)\" CFLAGS-tst-gettext5.c += -DOBJPFX=\"$(objpfx)\" CFLAGS-tst-gettext6.c += -DOBJPFX=\"$(objpfx)\" CFLAGS-tst-plural-eval.c += -DOBJPFX=\"$(objpfx)\" +CFLAGS-tst-gettext-path-traversal.c += -DOBJPFX=\"$(objpfx)\" ifeq ($(have-thread-library),yes) ifeq (yes,$(build-shared)) diff --git a/intl/dcigettext.c b/intl/dcigettext.c index 43b99c2dea..e9611dae7a 100644 --- a/intl/dcigettext.c +++ b/intl/dcigettext.c @@ -591,10 +591,10 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2, *cp++ = *categoryvalue++; *cp = '\0'; - /* When this is a SUID binary we must not allow accessing files - outside the dedicated directories. */ - if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale)) - /* Ignore this entry. */ + /* Do not allow accessing files outside the dedicated + directories. */ + if (IS_PATH_WITH_DIR (single_locale) + || strcmp (single_locale, "..") == 0) continue; } diff --git a/intl/tst-gettext-path-traversal.c b/intl/tst-gettext-path-traversal.c new file mode 100644 index 0000000000..0ddc0c8f70 --- /dev/null +++ b/intl/tst-gettext-path-traversal.c @@ -0,0 +1,83 @@ +/* Test that LANGUAGE values with path traversal are rejected [BZ #17142]. + Copyright (C) 2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +static const char *const domaindir = OBJPFX "domaindir"; +static const char *const localedir = OBJPFX "domaindir/de_DE"; +static const char *const msgdir = OBJPFX "domaindir/de_DE/LC_MESSAGES"; + +static int +do_test (void) +{ + unsetenv ("OUTPUT_CHARSET"); + /* LANGUAGE is only consulted if the locale is not "C" or "C.". + The catalog is in ISO-8859-1, as is the locale, so no conversion of + the translation takes place. */ + xsetlocale (LC_ALL, "de_DE.ISO-8859-1"); + textdomain ("codeset"); + + /* Verify that a legitimate LANGUAGE value produces the expected + translation. This exercises the normal lookup path and confirms + that the test .mo catalog is in place. */ + bindtextdomain ("codeset", domaindir); + setenv ("LANGUAGE", "de_DE", 1); + TEST_COMPARE_STRING (gettext ("cheese"), "K\344se"); + + /* A bare ".." must be rejected. Without the fix this would resolve + to msgdir/../LC_MESSAGES/codeset.mo, which is the real catalog, + so the translation would succeed. */ + bindtextdomain ("codeset", msgdir); + setenv ("LANGUAGE", "..", 1); + TEST_COMPARE_STRING (gettext ("cheese"), "cheese"); + + /* A relative path that leaves and re-enters the catalog directory + must be rejected. Without the fix this would resolve to + localedir/../de_DE/LC_MESSAGES/codeset.mo. */ + bindtextdomain ("codeset", localedir); + setenv ("LANGUAGE", "../de_DE", 1); + TEST_COMPARE_STRING (gettext ("cheese"), "cheese"); + + /* Multiple levels of directory traversal must be rejected. Without + the fix this would resolve to + msgdir/../../de_DE/LC_MESSAGES/codeset.mo. */ + bindtextdomain ("codeset", msgdir); + setenv ("LANGUAGE", "../../de_DE", 1); + TEST_COMPARE_STRING (gettext ("cheese"), "cheese"); + + /* Invalid entries in a colon-separated LANGUAGE list must be + skipped individually; valid entries that follow are still + used. */ + bindtextdomain ("codeset", domaindir); + setenv ("LANGUAGE", "..:../de_DE:de_DE", 1); + TEST_COMPARE_STRING (gettext ("cheese"), "K\344se"); + + /* Trailing "/.." must be rejected. Use localedir so the binding + changes, which invalidates the DCIGETTEXT result cache. */ + bindtextdomain ("codeset", localedir); + setenv ("LANGUAGE", "de_DE/..", 1); + TEST_COMPARE_STRING (gettext ("cheese"), "cheese"); + + return 0; +} + +#include