Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash with double-slashes in /etc/localtime on Python 3.9 #990

Open
torfsen opened this issue Mar 15, 2023 · 8 comments · May be fixed by #1006 or #1035
Open

Crash with double-slashes in /etc/localtime on Python 3.9 #990

torfsen opened this issue Mar 15, 2023 · 8 comments · May be fixed by #1006 or #1035

Comments

@torfsen
Copy link

torfsen commented Mar 15, 2023

Overview Description

Importing babel.localtime crashes on Python 3.9 if /etc/localtime contains double-slashes.

Steps to Reproduce

ls -lh /etc/localtime 
lrwxrwxrwx 1 root root 24 Mar  2 03:25 /etc/localtime -> /usr/share/zoneinfo//UTC

Note the double-slash, //UTC.

# python
Python 3.9.16 (main, Mar 12 2023, 19:18:41) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import babel.localtime
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/__init__.py", line 41, in <module>
    LOCALTZ = get_localzone()
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/__init__.py", line 37, in get_localzone
    return _get_localzone()
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/_unix.py", line 49, in _get_localzone
    tzinfo = _get_tzinfo(zone_name)
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/_helpers.py", line 21, in _get_tzinfo
    return zoneinfo.ZoneInfo(tzenv)
  File "/usr/local/lib/python3.9/zoneinfo/_tzpath.py", line 67, in find_tzfile
    _validate_tzfile_path(key)
  File "/usr/local/lib/python3.9/zoneinfo/_tzpath.py", line 81, in _validate_tzfile_path
    raise ValueError(
ValueError: ZoneInfo keys may not be absolute paths, got: /UTC
>>> import babel
>>> babel.__version__
'2.12.1'

Actual Results

Crash.

Expected Results

No crash.

Reproducibility

Crashes for me on Python 3.9 with babel 2.12.1. Works on Python 3.8.

The problem is that babel.localtime._unix._get_localzone has naive path handling.

Additional Information

I'm not sure whether babel is to blame here, but since this is working on Python 3.8 it would be nice to make babel more robust here. As far as I can tell, double slashes are valid in POSIX and should be treated as single slashes.

@alexmaragko
Copy link

Using babel version v2.11.0 with Python 3.9 does not give the error, so this becomes an issue in the latest versions.

@mdklatt
Copy link

mdklatt commented May 31, 2023

I just encountered this with Babel 2.12.1 running with Python 3.9 inside an Ubuntu 22.04 (Jammy) container. The problem is definitely the double slash in the /etc/localtime link. I don't know why the Ubuntu installer creates an irregular link like that, but it is indeed valid.

It looks like the problem is in localtime/_unix.py. The os.readlink() function is used to get the target of /etc/localtime. This does not normalize the path, so the time zone ID is being parsed incorrectly as /UTC instead of UTC.

def _get_localzone(_root: str = '/') -> datetime.tzinfo:
    """Tries to find the local timezone configuration.
    This method prefers finding the timezone name and passing that to
    zoneinfo or pytz, over passing in the localtime file, as in the later
    case the zoneinfo name is unknown.
    The parameter _root makes the function look for files like /etc/localtime
    beneath the _root directory. This is primarily used by the tests.
    In normal usage you call the function without parameters.
    """
    # <snip>

    # This is actually a pretty reliable way to test for the local time
    # zone on operating systems like OS X.  On OS X especially this is the
    # only one that actually works.
    try:
        link_dst = os.readlink('/etc/localtime')
    except OSError:
        pass

    # <snip>

@mdklatt
Copy link

mdklatt commented May 31, 2023

I think the solution to this is to replace os.readlink() with pathlib.Path().resolve(), which will normalize the path and remove the double slashes. I created a fork, but coming up with a test for this is tricky.

mdklatt added a commit to mdklatt/babel that referenced this issue Jun 1, 2023
Need some working tests before attempting to fix python-babel#990.

Refs python-babel#990
mdklatt added a commit to mdklatt/babel that referenced this issue Jun 1, 2023
Need some working tests before attempting to fix python-babel#990.

Refs python-babel#990
mdklatt added a commit to mdklatt/babel that referenced this issue Jun 1, 2023
In certain OS installations, the target of /etc/localtime contains
double slashes. This is a valid path, but not a valid zoneinfo key.
This fix replaces `os.readlink` with `os.path.realpath`, which is
guaranteed to return a normalized path.

Fixes python-babel#990
mdklatt added a commit to mdklatt/babel that referenced this issue Jun 1, 2023
In certain OS installations, the target of /etc/localtime contains
double slashes. This is a valid path, but not a valid zoneinfo key.
This fix replaces `os.readlink` with `os.path.realpath`, which is
guaranteed to return a normalized path.

Fixes python-babel#990
mdklatt added a commit to mdklatt/babel that referenced this issue Jun 1, 2023
Need some working tests before attempting to fix python-babel#990.

Refs python-babel#990
mdklatt added a commit to mdklatt/babel that referenced this issue Jun 1, 2023
In certain OS installations, the target of /etc/localtime contains
double slashes. This is a valid path, but not a valid zoneinfo key.
This fix replaces `os.readlink` with `os.path.realpath`, which is
guaranteed to return a normalized path.

Fixes python-babel#990
@mdklatt mdklatt linked a pull request Jun 1, 2023 that will close this issue
akx pushed a commit to mdklatt/babel that referenced this issue Jun 27, 2023
Need some working tests before attempting to fix python-babel#990.

Refs python-babel#990
akx pushed a commit to mdklatt/babel that referenced this issue Jun 27, 2023
In certain OS installations, the target of /etc/localtime contains
double slashes. This is a valid path, but not a valid zoneinfo key.
This fix replaces `os.readlink` with `os.path.realpath`, which is
guaranteed to return a normalized path.

Fixes python-babel#990
@natescherer
Copy link

Confirmed this is also a problem with Python 3.10 on Ubuntu.

@mdklatt
Copy link

mdklatt commented Oct 12, 2023

I submitted #1006 for this back in June. The PR could not be automatically merged because some unrelated broken tests were failing. It looks like it was manually merged by a maintainer, but I'm not sure what the current status is.

mdklatt added a commit to mdklatt/babel that referenced this issue Oct 12, 2023
Need some working tests before attempting to fix python-babel#990.

Refs python-babel#990
mdklatt added a commit to mdklatt/babel that referenced this issue Oct 12, 2023
In certain OS installations, the target of /etc/localtime contains
double slashes. This is a valid path, but not a valid zoneinfo key.
This fix replaces `os.readlink` with `os.path.realpath`, which is
guaranteed to return a normalized path.

Fixes python-babel#990
@mdklatt mdklatt linked a pull request Oct 12, 2023 that will close this issue
@mdklatt
Copy link

mdklatt commented Oct 12, 2023

The broken unit tests have been fixed upstream. I rebased my fixes onto master and submitted #1035. This should pass all automated checks, which will hopefully streamline the merge process and speed up the resolution of this issue.

@mdklatt
Copy link

mdklatt commented Oct 12, 2023

For anybody that needs a temporary workaround, I was able to fix this in an Ubuntu Docker image by recreating the /etc/localtime link to remove the double slashes:

rm -f /etc/localtime
ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime

@igormcoelho
Copy link

igormcoelho commented Oct 13, 2023

I'm having this issue with Ubuntu 22.04, python 3.10 and babel 2.13.

Traceback (most recent call last):
  File "/usr/local/bin/sphinx-build", line 5, in <module>
    from sphinx.cmd.build import main
  File "/usr/local/lib/python3.10/dist-packages/sphinx/cmd/build.py", line 25, in <module>
    from sphinx.application import Sphinx
  File "/usr/local/lib/python3.10/dist-packages/sphinx/application.py", line 32, in <module>
    from sphinx.config import Config
  File "/usr/local/lib/python3.10/dist-packages/sphinx/config.py", line 22, in <module>
    from sphinx.util.i18n import format_date
  File "/usr/local/lib/python3.10/dist-packages/sphinx/util/i18n.py", line 17, in <module>
    import babel.dates
  File "/usr/local/lib/python3.10/dist-packages/babel/dates.py", line 34, in <module>
    from babel import localtime
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/__init__.py", line 41, in <module>
    LOCALTZ = get_localzone()
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/__init__.py", line 37, in get_localzone
    return _get_localzone()
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/_unix.py", line 49, in _get_localzone
    tzinfo = _get_tzinfo(zone_name)
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/_helpers.py", line 21, in _get_tzinfo
    return zoneinfo.ZoneInfo(tzenv)
  File "/usr/lib/python3.10/zoneinfo/_tzpath.py", line 67, in find_tzfile
    _validate_tzfile_path(key)
  File "/usr/lib/python3.10/zoneinfo/_tzpath.py", line 81, in _validate_tzfile_path
    raise ValueError(
ValueError: ZoneInfo keys may not be absolute paths, got: /UTC

The strange thing is that my /etc/localtime does not have any slash... but it is quite strange anyway:

$ cat /etc/localtime 
TZif2UTCTZif2UTC
UTC0

Don't know how to fix it.

[EDIT] Sorry! Now I noticed that the double slash is on the filepath itself...

$ ls -la /etc/localtime
/etc/localtime -> /usr/share/zoneinfo//UTC

So this solution indeed fixed my error:

$ rm -f /etc/localtime
$ ln -s /usr/share/zoneinfo/UTC /etc/localtime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants