Skip to content

Conversation

@asottile
Copy link
Contributor

@asottile asottile commented Dec 10, 2017

This is a runtime attempt at implementing #4267

A few notes:

  • During review it was suggested to cache the failed lchmod attempts, however I don't think this is doable:
    • lchmod is used for both symlinks and not symlinks if follow_symlinks=False -- it works fine for normal files
    • detecting whether something is a symlink up front throws away all benefits of caching whether it fails as you still make a syscall
    • the code for fchmodat adjacent to the lchmod code serves as precedent for both of these decisions

https://bugs.python.org/issue31940

@fruch
Copy link

fruch commented Dec 11, 2017

@asottile nice, can't understand how there's a coredump in the test_shutil.py (under clang).
you tested this one only under alpine ?

@asottile asottile force-pushed the bpo-31940-ignore-faulty-lchmod-implementations branch from 85ba86a to d16cd68 Compare December 11, 2017 06:08
@asottile
Copy link
Contributor Author

@fruch woops, forgot an else -- silly macro trickery got me again :(

@asottile asottile force-pushed the bpo-31940-ignore-faulty-lchmod-implementations branch from d16cd68 to 5ed4b99 Compare December 11, 2017 06:23
@asottile
Copy link
Contributor Author

Please don't merge this yet, it depends on changes made in #4797

@asottile asottile force-pushed the bpo-31940-ignore-faulty-lchmod-implementations branch 2 times, most recently from a735a41 to 130279d Compare December 14, 2017 17:01
@asottile
Copy link
Contributor Author

I've rebased this now that #4797 is merged, should be ready for review again

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, there are two different error codes? Which one is raised on muslc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

muslc raises EOPNOTSUPP (errno 95) though on my platform they were #defined to the same thing. I simply picked the same errnos as fchmodat catches below -- I suspect implementations use fchmodat to implement lchown (or the other way around).

@asottile asottile force-pushed the bpo-31940-ignore-faulty-lchmod-implementations branch 3 times, most recently from 86425d2 to d64773b Compare December 15, 2017 15:36
@asottile asottile force-pushed the bpo-31940-ignore-faulty-lchmod-implementations branch from d64773b to 2e57192 Compare December 15, 2017 17:58
jacobsvante added a commit to jacobsvante/python that referenced this pull request Dec 15, 2017
@asottile
Copy link
Contributor Author

@vstinner would love another review :) no rush though!

@asottile
Copy link
Contributor Author

@vstinner friendly ping :)

@asottile
Copy link
Contributor Author

Looking through devguide -- maybe @giampaolo can review the shutil changes?

dtzWill added a commit to dtzWill/nixpkgs that referenced this pull request Apr 26, 2018
upstream issue:
https://bugs.python.org/issue31940

There are two PR's proposed to fix this,
but both seem to be stalling waiting for review.

I previously used what appears to be the favored
of the two approaches[1] to fix this,
with plan of keeping it musl-only until PR was merged.

However, while writing up a commit message
explaining the problem and why it needed fixing...

I investigated a bit and found it increasingly
hard to justify anything other than ...
simply not using lchmod.

Here's what I found:
* lchmod is non-POSIX, seems BSD-only these days
* Functionality of lchmod isn't supported on Linux
  * best scenario on Linux would be an error
* POSIX does provide lchmod-esque functionality
  with fchmodat(), which AFAICT is generally preferred.
* Python intentionally overlooks fchmodat()[2]
  electing instead to use lchmod() behavior
  as a proxy for whether fchmodat() "works".
  I'm not sure I follow their reasoning...
* both glibc and musl provide lchmod impls:
  * glibc returns ENOSYS "not implemented"
  * musl implements lchmod with fchmodat(),
    and so returns EOPNOTSUPP "op not supported"
* Python doesn't expect EOPNOTSUPP from lchmod,
  since it's not valid on BSD's lchmod.
* "configure" doesn't actually check lchmod usefully,
  instead checks for glibc preprocessor defines
  to indicate if the function is just a stub[3];
  somewhat fittingly, if the magic macros are defined
  then the next line of the C source is "choke me",
  causing the compiler to trip, fall, and point
  a finger at whatever is near where it ends up.
  (somewhat amusing, but AFAIK effective way to get an error :P)

I'm leaving out links to threads on mailing lists and such,
but for now I hope I've convinced you
(or to those reading commit history: explained my reasons)
that this is a bit of a mess[4].

And so instead of making a big mess messier,
and with hopes of never thinking about this again,
I propose we simply tell Python "don't use lchmod" on Linux.

[1] python/cpython#4783
[2] https://github.com/python/cpython/blob/28453feaa8d88bbcbf6d834b1d5ca396d17265f2/Lib/os.py#L144
[3] https://github.com/python/cpython/blob/28453feaa8d88bbcbf6d834b1d5ca396d17265f2/configure#L2198
[4] Messes happen, no good intention goes unpunished :).
dtzWill added a commit to dtzWill/nixpkgs that referenced this pull request Apr 26, 2018
upstream issue:
https://bugs.python.org/issue31940

There are two PR's proposed to fix this,
but both seem to be stalling waiting for review.

I previously used what appears to be the favored
of the two approaches[1] to fix this,
with plan of keeping it musl-only until PR was merged.

However, while writing up a commit message
explaining the problem and why it needed fixing...

I investigated a bit and found it increasingly
hard to justify anything other than ...
simply not using lchmod.

Here's what I found:
* lchmod is non-POSIX, seems BSD-only these days
* Functionality of lchmod isn't supported on Linux
  * best scenario on Linux would be an error
* POSIX does provide lchmod-esque functionality
  with fchmodat(), which AFAICT is generally preferred.
* Python intentionally overlooks fchmodat()[2]
  electing instead to use lchmod() behavior
  as a proxy for whether fchmodat() "works".
  I'm not sure I follow their reasoning...
* both glibc and musl provide lchmod impls:
  * glibc returns ENOSYS "not implemented"
  * musl implements lchmod with fchmodat(),
    and so returns EOPNOTSUPP "op not supported"
* Python doesn't expect EOPNOTSUPP from lchmod,
  since it's not valid on BSD's lchmod.
* "configure" doesn't actually check lchmod usefully,
  instead checks for glibc preprocessor defines
  to indicate if the function is just a stub[3];
  somewhat fittingly, if the magic macros are defined
  then the next line of the C source is "choke me",
  causing the compiler to trip, fall, and point
  a finger at whatever is near where it ends up.
  (somewhat amusing, but AFAIK effective way to get an error :P)

I'm leaving out links to threads on mailing lists and such,
but for now I hope I've convinced you
(or to those reading commit history: explained my reasons)
that this is a bit of a mess[4].

And so instead of making a big mess messier,
and with hopes of never thinking about this again,
I propose we simply tell Python "don't use lchmod" on Linux.

[1] python/cpython#4783
[2] https://github.com/python/cpython/blob/28453feaa8d88bbcbf6d834b1d5ca396d17265f2/Lib/os.py#L144
[3] https://github.com/python/cpython/blob/28453feaa8d88bbcbf6d834b1d5ca396d17265f2/configure#L2198
[4] Messes happen, no good intention goes unpunished :).
dtzWill added a commit to dtzWill/nixpkgs that referenced this pull request Apr 26, 2018
upstream issue:
https://bugs.python.org/issue31940

There are two PR's proposed to fix this,
but both seem to be stalling waiting for review.

I previously used what appears to be the favored
of the two approaches[1] to fix this,
with plan of keeping it musl-only until PR was merged.

However, while writing up a commit message
explaining the problem and why it needed fixing...

I investigated a bit and found it increasingly
hard to justify anything other than ...
simply not using lchmod.

Here's what I found:
* lchmod is non-POSIX, seems BSD-only these days
* Functionality of lchmod isn't supported on Linux
  * best scenario on Linux would be an error
* POSIX does provide lchmod-esque functionality
  with fchmodat(), which AFAICT is generally preferred.
* Python intentionally overlooks fchmodat()[2]
  electing instead to use lchmod() behavior
  as a proxy for whether fchmodat() "works".
  I'm not sure I follow their reasoning...
* both glibc and musl provide lchmod impls:
  * glibc returns ENOSYS "not implemented"
  * musl implements lchmod with fchmodat(),
    and so returns EOPNOTSUPP "op not supported"
* Python doesn't expect EOPNOTSUPP from lchmod,
  since it's not valid on BSD's lchmod.
* "configure" doesn't actually check lchmod usefully,
  instead checks for glibc preprocessor defines
  to indicate if the function is just a stub[3];
  somewhat fittingly, if the magic macros are defined
  then the next line of the C source is "choke me",
  causing the compiler to trip, fall, and point
  a finger at whatever is near where it ends up.
  (somewhat amusing, but AFAIK effective way to get an error :P)

I'm leaving out links to threads on mailing lists and such,
but for now I hope I've convinced you
(or to those reading commit history: explained my reasons)
that this is a bit of a mess[4].

And so instead of making a big mess messier,
and with hopes of never thinking about this again,
I propose we simply tell Python "don't use lchmod" on Linux.

[1] python/cpython#4783
[2] https://github.com/python/cpython/blob/28453feaa8d88bbcbf6d834b1d5ca396d17265f2/Lib/os.py#L144
[3] https://github.com/python/cpython/blob/28453feaa8d88bbcbf6d834b1d5ca396d17265f2/configure#L2198
[4] Messes happen, no good intention goes unpunished :).
if not support.can_symlink() or not hasattr(os, 'lchmod'):
return False

fname = tempfile.mktemp()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be mkstemp? mktemp seems to be deprecated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was copied from adjacent test (in the original PR). It's not exactly that mktemp is deprecated, it's that it's insecure. However in this case, the same security problem would exist since it's essentially:

  1. mkstemp
  2. unlink the file made by mkstemp
  3. symlink to the location made by mkstemp

There's a race between 2 and 3 where something could create a file

Fortunately it doesn't really matter since it's just a test.

@asottile asottile force-pushed the bpo-31940-ignore-faulty-lchmod-implementations branch from 2e57192 to 46ea965 Compare November 26, 2018 17:29
@asottile
Copy link
Contributor Author

Almost this branch's birthday -- rebased to resolve conflicts

@fruch
Copy link

fruch commented Nov 26, 2018

@asottile anything I could do to help ?

@asottile
Copy link
Contributor Author

@fruch iirc an approving review (even from an outsider) will move the state from [awaiting review] to [awaiting core review] which might (???) gather more attention from core devs

Copy link

@TheKevJames TheKevJames left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changeset LGTM, but I have not worked with this chunk of the cpython codebase before. Would be great to get a core contributor review as well!

@jdubost
Copy link

jdubost commented Nov 27, 2018

Dunno if this can help but i encountered the issue (https://bugs.python.org/issue31940) with a python-3.6 tox environment within an alpine image python:2.7-alpine3.6 (+ python-3.6.5 installed manually)
Then i upgraded to a python:3.6.7-alpine3.6 image (python-3.6.7 native) -> it solved the issue.

Since tox on a python2 environment works well on python:3.6.7-alpine3.6, i now use this image for all my tests.

Edit :

  • python:3.6.7-alpine3.6 + python downgrade to 3.6.5 : NOK
  • python:3.6.7-alpine3.6 : OK
  • python:2.7-alpine3.6 + python 3.6.5 : NOK

@asottile asottile force-pushed the bpo-31940-ignore-faulty-lchmod-implementations branch from 46ea965 to 3460a76 Compare November 27, 2018 17:34
@asottile
Copy link
Contributor Author

@jdubost looks like alpine is working around this bug by manually turning off HAVE_LCHMOD:

python:3.6.7-alpine3.6$ grep HAVE_LCHMOD /usr/local/include/python3.6m/pyconfig.h 
/* #undef HAVE_LCHMOD */
python:3.6.1-alpine3.6$ grep HAVE_LCHMOD /usr/local/include/python3.6m/pyconfig.h 
#define HAVE_LCHMOD 1

It looks like @benjaminp forced this to off on all linuxes in 40caa05

which looks eerily similar to my rejected patch in #4267 😭

@benjaminp
Copy link
Contributor

Hmm, I didn't see any of this.

I don't understand the objection to a compile-time check. Whether you're targeting Linux or not is something you must know at compile time. It would be one thing if an operating system existed that sometimes returned ENOSYS from lchmod. But, I know of no such system.

@asottile
Copy link
Contributor Author

unclear as well. I guess since this is solved I can close this and we can resolve the linked issue.

I think some of the changes in the patch are useful so I'll make a separate bpo and propose them (notably, os.stat / os.chmod are now always present so the lookup(...) calls are unnecessary)

@asottile asottile closed this Nov 28, 2018
@asottile asottile deleted the bpo-31940-ignore-faulty-lchmod-implementations branch November 28, 2018 05:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants