[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#931456: libc6: uselocale(3) does not invalidate gettext translation cache



Package: libc6
Version: 2.28-10
Severity: normal

As discussed in https://bugs.debian.org/931139 and
https://bugs.debian.org/924657 glibc has a cache of already
loaded gettext translations that gets invalidated (by incrementing
_nl_msg_cat_cntr) in setlocale(3), bindtextdomain(3) and textdomain(3)
but not uselocale(3). This seems like an oversight.  No matter how the
locale gets changed, the cache should be invalidated.

The attached test case demonstrates this.  When called without arguments,
it uses uselocale(3) so the first translation gets used for all the
languages. This is the "buggy" behaviour.

When called with any arguments, it uses setlocale(3) and works as
intended, outputting the string in three different languages.

The issue is only visible when using the LANGUAGE environment variable
to select the translation. Using LC_MESSAGES, LANG or LC_ALL hides
the issue. This is because LC_*/LANG affect the locale value that is
used to compute the cache hashing key inside glibc (in DCIGETTEXT()),
while LANGUAGE doesn't.

Any insight would be welcome. I can take this upstream but would of
course be happy for the maintainers to do that :)

Thanks for your work on Debian,
-- 
Niko Tyni   ntyni@debian.org
#include <stdio.h>
#include <stdlib.h>

#include <libintl.h>
#include <locale.h>

int main(int argc, char **argv)
{
  locale_t loc;
  int i=0;

  /* The C locale is special cased in glibc to not look at LANGUAGE
     so we set C.UTF-8 as the base locale instead */
  setenv("LANG", "C.UTF-8", 1);

  char *locales[] = { "fi_FI", "fr_FR", "en_US", NULL };

  for (i=0; locales[i] != NULL; i++) {
    setenv("LANGUAGE", locales[i], 1);
    if (argc > 1) {
        /* cache invalidated correctly, no bug */
        setlocale(LC_MESSAGES, "");
    } else {
        /* cache not invalidated, first translation stays cached */
        loc = newlocale(LC_MESSAGES_MASK, "", (locale_t) 0);
        if (loc == (locale_t) 0)
            perror("newlocale");
        uselocale(loc);
    }
    printf("%s\n", dgettext("bash", "syntax error"));
  }

  return EXIT_SUCCESS;
}

Reply to: