LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v3 0/6] Remove x86-specific code from generic headers
From: Lendacky, Thomas @ 2019-07-19 13:12 UTC (permalink / raw)
  To: Thiago Jung Bauermann
  Cc: linux-s390@vger.kernel.org, Mike Anderson, Konrad Rzeszutek Wilk,
	Robin Murphy, x86@kernel.org, Ram Pai,
	linux-kernel@vger.kernel.org, Alexey Dobriyan, Halil Pasic,
	iommu@lists.linux-foundation.org, Ingo Molnar, Borislav Petkov,
	H. Peter Anvin, linux-fsdevel@vger.kernel.org, Thomas Gleixner,
	linuxppc-dev@lists.ozlabs.org, Christoph Hellwig,
	Marek Szyprowski
In-Reply-To: <87a7db3z68.fsf@morokweng.localdomain>

On 7/18/19 2:44 PM, Thiago Jung Bauermann wrote:
> 
> Lendacky, Thomas <Thomas.Lendacky@amd.com> writes:
> 
>> On 7/17/19 10:28 PM, Thiago Jung Bauermann wrote:
>>> Hello,
>>>
>>> This version is mostly about splitting up patch 2/3 into three separate
>>> patches, as suggested by Christoph Hellwig. Two other changes are a fix in
>>> patch 1 which wasn't selecting ARCH_HAS_MEM_ENCRYPT for s390 spotted by
>>> Janani and removal of sme_active and sev_active symbol exports as suggested
>>> by Christoph Hellwig.
>>>
>>> These patches are applied on top of today's dma-mapping/for-next.
>>>
>>> I don't have a way to test SME, SEV, nor s390's PEF so the patches have only
>>> been build tested.
>>
>> I'll try and get this tested quickly to be sure everything works for SME
>> and SEV.

Built and tested both SME and SEV and everything appears to be working
well (not extensive testing, but should be good enough).

Thanks,
Tom

> 
> Thanks! And thanks for reviewing the patches.
> 

^ permalink raw reply

* Re: [PATCH v3 5/7] kexec_elf: remove elf_addr_to_cpu macro
From: Michael Ellerman @ 2019-07-19 14:06 UTC (permalink / raw)
  To: Sven Schnelle; +Cc: kexec, deller, linuxppc-dev
In-Reply-To: <20190715072417.GA25659@t470p.stackframe.org>

Sven Schnelle <svens@stackframe.org> writes:
> Hi Michael,
>
> On Thu, Jul 11, 2019 at 09:08:51PM +1000, Michael Ellerman wrote:
>> Sven Schnelle <svens@stackframe.org> writes:
>> > On Wed, Jul 10, 2019 at 05:09:29PM +0200, Christophe Leroy wrote:
>> >> Le 10/07/2019 à 16:29, Sven Schnelle a écrit :
>> >> > It had only one definition, so just use the function directly.
>> >> 
>> >> It had only one definition because it was for ppc64 only.
>> >> But as far as I understand (at least from the name of the new file), you
>> >> want it to be generic, don't you ? Therefore I get on 32 bits it would be
>> >> elf32_to_cpu().
>> >
>> > That brings up the question whether we need those endianess conversions. I would
>> > assume that the ELF file has always the same endianess as the running kernel. So
>> > i think we could just drop them. What do you think?
>> 
>> We should be able to kexec from big to little endian or vice versa, so
>> they are necessary.
>
> I'll update the patch to check for a needed 32/64 bit conversion during runtime,
> so we can also kexec from 32 to 64 bit kernels and vice versa. Don't know
> whether that's possible on powerpc, but at least on parisc it is.

On some of the Freescale (NXP) machines that should actually be
possible, the hardware can run a 64 or 32-bit kernel, but I'm not sure
if anyone has actually tested kexec'ing from one to the other.

cheers

^ permalink raw reply

* Re: [PATCH v2] powerpc: slightly improve cache helpers
From: Segher Boessenkool @ 2019-07-19 15:23 UTC (permalink / raw)
  To: Nathan Chancellor
  Cc: linux-kernel, clang-built-linux, Paul Mackerras, linuxppc-dev
In-Reply-To: <20190719032456.GA14108@archlinux-threadripper>

On Thu, Jul 18, 2019 at 08:24:56PM -0700, Nathan Chancellor wrote:
> On Mon, Jul 08, 2019 at 11:49:52PM -0700, Nathan Chancellor wrote:
> > On Tue, Jul 09, 2019 at 07:04:43AM +0200, Christophe Leroy wrote:
> > > Is that a Clang bug ?
> > 
> > No idea, it happens with clang-8 and clang-9 though (pretty sure there
> > were fixes for PowerPC in clang-8 so something before it probably won't
> > work but I haven't tried).
> > 
> > > 
> > > Do you have a disassembly of the code both with and without this patch in
> > > order to compare ?
> > 
> > I can give you whatever disassembly you want (or I can upload the raw
> > files if that is easier).
> 
> What disassembly/files did you need to start taking a look at this? I
> can upload/send whatever you need.

A before and after of *only this patch*.  And then look at what changed;
it maybe be obvious what is the problem to you, as well, so look at it
yourself first?


Segher

^ permalink raw reply

* Re: [PATCH v3 0/6] Remove x86-specific code from generic headers
From: Thiago Jung Bauermann @ 2019-07-19 15:45 UTC (permalink / raw)
  To: Lendacky, Thomas
  Cc: linux-s390@vger.kernel.org, Mike Anderson, Konrad Rzeszutek Wilk,
	Robin Murphy, x86@kernel.org, Ram Pai,
	linux-kernel@vger.kernel.org, Alexey Dobriyan, Halil Pasic,
	iommu@lists.linux-foundation.org, Ingo Molnar, Borislav Petkov,
	H. Peter Anvin, linux-fsdevel@vger.kernel.org, Thomas Gleixner,
	linuxppc-dev@lists.ozlabs.org, Christoph Hellwig,
	Marek Szyprowski
In-Reply-To: <879a196a-1d92-931d-88f4-6ce17a09cf20@amd.com>


Lendacky, Thomas <Thomas.Lendacky@amd.com> writes:

> On 7/18/19 2:44 PM, Thiago Jung Bauermann wrote:
>> 
>> Lendacky, Thomas <Thomas.Lendacky@amd.com> writes:
>> 
>>> On 7/17/19 10:28 PM, Thiago Jung Bauermann wrote:
>>>> Hello,
>>>>
>>>> This version is mostly about splitting up patch 2/3 into three separate
>>>> patches, as suggested by Christoph Hellwig. Two other changes are a fix in
>>>> patch 1 which wasn't selecting ARCH_HAS_MEM_ENCRYPT for s390 spotted by
>>>> Janani and removal of sme_active and sev_active symbol exports as suggested
>>>> by Christoph Hellwig.
>>>>
>>>> These patches are applied on top of today's dma-mapping/for-next.
>>>>
>>>> I don't have a way to test SME, SEV, nor s390's PEF so the patches have only
>>>> been build tested.
>>>
>>> I'll try and get this tested quickly to be sure everything works for SME
>>> and SEV.
>
> Built and tested both SME and SEV and everything appears to be working
> well (not extensive testing, but should be good enough).

Great news. Thanks for testing!

-- 
Thiago Jung Bauermann
IBM Linux Technology Center


^ permalink raw reply

* Re: [PATCH v3 5/6] fs/core/vmcore: Move sev_active() reference to x86 arch code
From: Thiago Jung Bauermann @ 2019-07-19 15:47 UTC (permalink / raw)
  To: lijiang
  Cc: Lendacky, Thomas, Mike Anderson, Baoquan He,
	Konrad Rzeszutek Wilk, linux-s390@vger.kernel.org, Robin Murphy,
	x86@kernel.org, Ram Pai, linux-kernel@vger.kernel.org,
	Alexey Dobriyan, Halil Pasic, iommu@lists.linux-foundation.org,
	Ingo Molnar, Borislav Petkov, H. Peter Anvin,
	linux-fsdevel@vger.kernel.org, Thomas Gleixner,
	linuxppc-dev@lists.ozlabs.org, Christoph Hellwig,
	Marek Szyprowski
In-Reply-To: <c85ae8ff-3b7b-88bf-6b6a-c41b159c9cc2@redhat.com>


Hello Lianbo,

lijiang <lijiang@redhat.com> writes:

> 在 2019年07月19日 01:47, Lendacky, Thomas 写道:
>> On 7/17/19 10:28 PM, Thiago Jung Bauermann wrote:
>>> Secure Encrypted Virtualization is an x86-specific feature, so it shouldn't
>>> appear in generic kernel code because it forces non-x86 architectures to
>>> define the sev_active() function, which doesn't make a lot of sense.
>>>
>>> To solve this problem, add an x86 elfcorehdr_read() function to override
>>> the generic weak implementation. To do that, it's necessary to make
>>> read_from_oldmem() public so that it can be used outside of vmcore.c.
>>>
>>> Also, remove the export for sev_active() since it's only used in files that
>>> won't be built as modules.
>>>
>>> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.ibm.com>
>> 
>> Adding Lianbo and Baoquan, who recently worked on this, for their review.
>> 
>
> This change looks good to me.
>
> Reviewed-by: Lianbo Jiang <lijiang@redhat.com>

Thanks for your review!

-- 
Thiago Jung Bauermann
IBM Linux Technology Center


^ permalink raw reply

* Re: [PATCH v2] powerpc: slightly improve cache helpers
From: Nathan Chancellor @ 2019-07-19 16:04 UTC (permalink / raw)
  To: Segher Boessenkool
  Cc: linux-kernel, clang-built-linux, Paul Mackerras, linuxppc-dev
In-Reply-To: <20190719152303.GA20882@gate.crashing.org>

On Fri, Jul 19, 2019 at 10:23:03AM -0500, Segher Boessenkool wrote:
> On Thu, Jul 18, 2019 at 08:24:56PM -0700, Nathan Chancellor wrote:
> > On Mon, Jul 08, 2019 at 11:49:52PM -0700, Nathan Chancellor wrote:
> > > On Tue, Jul 09, 2019 at 07:04:43AM +0200, Christophe Leroy wrote:
> > > > Is that a Clang bug ?
> > > 
> > > No idea, it happens with clang-8 and clang-9 though (pretty sure there
> > > were fixes for PowerPC in clang-8 so something before it probably won't
> > > work but I haven't tried).
> > > 
> > > > 
> > > > Do you have a disassembly of the code both with and without this patch in
> > > > order to compare ?
> > > 
> > > I can give you whatever disassembly you want (or I can upload the raw
> > > files if that is easier).
> > 
> > What disassembly/files did you need to start taking a look at this? I
> > can upload/send whatever you need.
> 
> A before and after of *only this patch*.  And then look at what changed;
> it maybe be obvious what is the problem to you, as well, so look at it
> yourself first?
> 
> 
> Segher

Thanks, I will go ahead and disassemble the full kernel given that those
helpers are everywhere and see what I can find. I'll reach out again if
I can't come up with anything.

Thanks for the advice!
Nathan

^ permalink raw reply

* [PATCH v10 0/9] namei: openat2(2) path resolution restrictions
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, containers, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	Chanho Min

This patch is being developed here (with snapshots of each series
version being stashed in separate branches with names of the form
"resolveat/vX-summary"):
    <https://github.com/cyphar/linux/tree/resolveat/master>

Patch changelog:
 v10:
    * Ensure that unlazy_walk() will fail if we are in a scoped walk and
      the caller has zeroed nd->root (this happens in a few places, I'm
      not sure why because unlazy_walk() does legitimize_path()
      already). In this case we need to go through path_init() again to
      reset it (otherwise we will have a breakout because set_root()
      will breakout).
      * Also add a WARN_ON (and return -ENOTRECOVERABLE) if
        LOOKUP_IN_ROOT is set and we are in set_root() -- which should
        never happen and will cause a breakout.
    * Make changes suggested by Al Viro:
      * Remove nd->{opath_mask,acc_mode} by moving all of the magic-link
        permission logic be done after trailing_symlink() (with
        trailing_magiclink()) only within path_openat().
      * Introduce LOOKUP_MAGICLINK_JUMPED to be able to detect
        magic-link jumps done with nd_jump_link() (so we don't end up
        blocking other LOOKUP_JUMPED cases).
      * Simplify all of the path_init() changes to make the code far
        less confusing. dirfd_path_init() turns out to be un-necessary.
    * Make openat2(2) also -EINVAL on unknown how->flags.
      [Dmitry V. Levin]
    * Clean up bad definitions of O_EMPTYPATH on architectures where O_*
      flags are subtly different to <asm-generic/fcntl.h>.
    * Switch away from passing a struct to build_open_flags() and
      instead just copy the one field we need to temporarily modify
      (how->flags). Also fix a bug in OPENHOW_MODE. [Rasmus Villemoes]
    * Fix syscall linkages and switch to 437. [Arnd Bergmann]
    * Clean up text in commit messages and the cover-letter.
      [Rolf Eike Beer]
    * Fix openat2 selftest makefile. [Michael Ellerman]

The need for some sort of control over VFS's path resolution (to avoid
malicious paths resulting in inadvertent breakouts) has been a very
long-standing desire of many userspace applications. This patchset is a
revival of Al Viro's old AT_NO_JUMPS[1,2] patchset (which was a variant
of David Drysdale's O_BENEATH patchset[3] which was a spin-off of the
Capsicum project[4]) with a few additions and changes made based on the
previous discussion within [5] as well as others I felt were useful.

In line with the conclusions of the original discussion of AT_NO_JUMPS,
the flag has been split up into separate flags. However, instead of
being an openat(2) flag it is provided through a new syscall openat2(2)
which provides an alternative way to get an O_PATH file descriptor (the
reasoning for doing this is included in the patch description). The
following new LOOKUP_* flags are added:

  * LOOKUP_NO_XDEV blocks all mountpoint crossings (upwards, downwards,
    or through absolute links). Absolute pathnames alone in openat(2) do
    not trigger this.

  * LOOKUP_NO_MAGICLINKS blocks resolution through /proc/$pid/fd-style
    links. This is done by blocking the usage of nd_jump_link() during
    resolution in a filesystem. The term "magic-links" is used to match
    with the only reference to these links in Documentation/, but I'm
    happy to change the name.

    It should be noted that this is different to the scope of
    ~LOOKUP_FOLLOW in that it applies to all path components. However,
    you can do openat2(NO_FOLLOW|NO_MAGICLINKS) on a magic-link and it
    will *not* fail (assuming that no parent component was a
    magic-link), and you will have an fd for the magic-link.

  * LOOKUP_BENEATH disallows escapes to outside the starting dirfd's
    tree, using techniques such as ".." or absolute links. Absolute
    paths in openat(2) are also disallowed. Conceptually this flag is to
    ensure you "stay below" a certain point in the filesystem tree --
    but this requires some additional to protect against various races
    that would allow escape using "..".

    Currently LOOKUP_BENEATH implies LOOKUP_NO_MAGICLINKS, because it
    can trivially beam you around the filesystem (breaking the
    protection). In future, there might be similar safety checks done as
    in LOOKUP_IN_ROOT, but that requires more discussion.

In addition, two new flags are added that expand on the above ideas:

  * LOOKUP_NO_SYMLINKS does what it says on the tin. No symlink
    resolution is allowed at all, including magic-links. Just as with
    LOOKUP_NO_MAGICLINKS this can still be used with NOFOLLOW to open an
    fd for the symlink as long as no parent path had a symlink
    component.

  * LOOKUP_IN_ROOT is an extension of LOOKUP_BENEATH that, rather than
    blocking attempts to move past the root, forces all such movements
    to be scoped to the starting point. This provides chroot(2)-like
    protection but without the cost of a chroot(2) for each filesystem
    operation, as well as being safe against race attacks that chroot(2)
    is not.

    If a race is detected (as with LOOKUP_BENEATH) then an error is
    generated, and similar to LOOKUP_BENEATH it is not permitted to cross
    magic-links with LOOKUP_IN_ROOT.

    The primary need for this is from container runtimes, which
    currently need to do symlink scoping in userspace[6] when opening
    paths in a potentially malicious container. There is a long list of
    CVEs that could have bene mitigated by having RESOLVE_THIS_ROOT
    (such as CVE-2017-1002101, CVE-2017-1002102, CVE-2018-15664, and
    CVE-2019-5736, just to name a few).

And further, several semantics of file descriptor "re-opening" are now
changed to prevent attacks like CVE-2019-5736 by restricting how
magic-links can be resolved (based on their mode). This required some
other changes to the semantics of the modes of O_PATH file descriptor's
associated /proc/self/fd magic-links. openat2(2) has the ability to
further restrict re-opening of its own O_PATH fds, so that users can
make even better use of this feature.

Finally, O_EMPTYPATH was added so that users can do /proc/self/fd-style
re-opening without depending on procfs. The new restricted semantics for
magic-links are applied here too.

In order to make all of the above more usable, I'm working on
libpathrs[7] which is a C-friendly library for safe path resolution. It
features a userspace-emulated backend if the kernel doesn't support
openat2(2). Hopefully we can get userspace to switch to using it, and
thus get openat2(2) support for free once it's ready.

Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: David Howells <dhowells@redhat.com>
Cc: Jann Horn <jannh@google.com>
Cc: Christian Brauner <christian@brauner.io>
Cc: David Drysdale <drysdale@google.com>
Cc: Tycho Andersen <tycho@tycho.ws>
Cc: Kees Cook <keescook@chromium.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: <containers@lists.linux-foundation.org>
Cc: <linux-fsdevel@vger.kernel.org>
Cc: <linux-api@vger.kernel.org>

[1]: https://lwn.net/Articles/721443/
[2]: https://lore.kernel.org/patchwork/patch/784221/
[3]: https://lwn.net/Articles/619151/
[4]: https://lwn.net/Articles/603929/
[5]: https://lwn.net/Articles/723057/
[6]: https://github.com/cyphar/filepath-securejoin
[7]: https://github.com/openSUSE/libpathrs

Aleksa Sarai (9):
  namei: obey trailing magic-link DAC permissions
  procfs: switch magic-link modes to be more sane
  open: O_EMPTYPATH: procfs-less file descriptor re-opening
  namei: O_BENEATH-style path resolution flags
  namei: LOOKUP_IN_ROOT: chroot-like path resolution
  namei: aggressively check for nd->root escape on ".." resolution
  open: openat2(2) syscall
  kselftest: save-and-restore errno to allow for %m formatting
  selftests: add openat2(2) selftests

 Documentation/filesystems/path-lookup.rst     |  12 +-
 arch/alpha/include/uapi/asm/fcntl.h           |   1 +
 arch/alpha/kernel/syscalls/syscall.tbl        |   1 +
 arch/arm/tools/syscall.tbl                    |   1 +
 arch/arm64/include/asm/unistd.h               |   2 +-
 arch/arm64/include/asm/unistd32.h             |   2 +
 arch/ia64/kernel/syscalls/syscall.tbl         |   1 +
 arch/m68k/kernel/syscalls/syscall.tbl         |   1 +
 arch/microblaze/kernel/syscalls/syscall.tbl   |   1 +
 arch/mips/kernel/syscalls/syscall_n32.tbl     |   1 +
 arch/mips/kernel/syscalls/syscall_n64.tbl     |   1 +
 arch/mips/kernel/syscalls/syscall_o32.tbl     |   1 +
 arch/parisc/include/uapi/asm/fcntl.h          |  39 +-
 arch/parisc/kernel/syscalls/syscall.tbl       |   1 +
 arch/powerpc/kernel/syscalls/syscall.tbl      |   1 +
 arch/s390/kernel/syscalls/syscall.tbl         |   1 +
 arch/sh/kernel/syscalls/syscall.tbl           |   1 +
 arch/sparc/include/uapi/asm/fcntl.h           |   1 +
 arch/sparc/kernel/syscalls/syscall.tbl        |   1 +
 arch/x86/entry/syscalls/syscall_32.tbl        |   1 +
 arch/x86/entry/syscalls/syscall_64.tbl        |   1 +
 arch/xtensa/kernel/syscalls/syscall.tbl       |   1 +
 fs/fcntl.c                                    |   2 +-
 fs/internal.h                                 |   1 +
 fs/namei.c                                    | 270 ++++++++++--
 fs/open.c                                     | 112 ++++-
 fs/proc/base.c                                |  20 +-
 fs/proc/fd.c                                  |  23 +-
 fs/proc/namespaces.c                          |   2 +-
 include/linux/fcntl.h                         |  17 +-
 include/linux/fs.h                            |   8 +-
 include/linux/namei.h                         |   9 +
 include/linux/syscalls.h                      |  17 +-
 include/uapi/asm-generic/fcntl.h              |   4 +
 include/uapi/asm-generic/unistd.h             |   4 +-
 include/uapi/linux/fcntl.h                    |  42 ++
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/kselftest.h           |  15 +
 tools/testing/selftests/memfd/memfd_test.c    |   7 +-
 tools/testing/selftests/openat2/.gitignore    |   1 +
 tools/testing/selftests/openat2/Makefile      |   8 +
 tools/testing/selftests/openat2/helpers.c     | 162 +++++++
 tools/testing/selftests/openat2/helpers.h     | 114 +++++
 .../testing/selftests/openat2/linkmode_test.c | 326 ++++++++++++++
 .../selftests/openat2/rename_attack_test.c    | 124 ++++++
 .../testing/selftests/openat2/resolve_test.c  | 397 ++++++++++++++++++
 46 files changed, 1652 insertions(+), 107 deletions(-)
 create mode 100644 tools/testing/selftests/openat2/.gitignore
 create mode 100644 tools/testing/selftests/openat2/Makefile
 create mode 100644 tools/testing/selftests/openat2/helpers.c
 create mode 100644 tools/testing/selftests/openat2/helpers.h
 create mode 100644 tools/testing/selftests/openat2/linkmode_test.c
 create mode 100644 tools/testing/selftests/openat2/rename_attack_test.c
 create mode 100644 tools/testing/selftests/openat2/resolve_test.c

-- 
2.22.0


^ permalink raw reply

* [PATCH v10 1/9] namei: obey trailing magic-link DAC permissions
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

The ability for userspace to "re-open" file descriptors through
/proc/self/fd has been a very useful tool for all sorts of usecases
(container runtimes are one common example). However, the current
interface for doing this has resulted in some pretty subtle security
holes. Userspace can re-open a file descriptor with more permissions
than the original, which can result in cases such as /proc/$pid/exe
being re-opened O_RDWR at a later date even though (by definition)
/proc/$pid/exe cannot be opened for writing. When combined with O_PATH
the results can get even more confusing.

We cannot block this outright. Aside from userspace already depending on
it, it's a useful feature which can actually increase the security of
userspace. For instance, LXC keeps an O_PATH of the container's
/dev/pts/ptmx that gets re-opened to create new ptys and then uses
TIOCGPTPEER to get the slave end. This allows for pty allocation without
resolving paths inside an (untrusted) container's rootfs. There isn't a
trivial way of doing this that is as straight-forward and safe as O_PATH
re-opening.

Instead we have to restrict it in such a way that it doesn't break
(good) users but does block potential attackers. The solution applied in
this patch is to restrict *re-opening* (not resolution through)
magic-links by requiring that mode of the link be obeyed. Normal
symlinks have modes of a+rwx but magic-links have other modes. These
magic-link modes were historically ignored during path resolution, but
they've now been re-purposed for more useful ends.

It is also necessary to define semantics for the mode of an O_PATH
descriptor, since re-opening a magic-link through an O_PATH needs to be
just as restricted as the corresponding magic-link -- otherwise the
above protection can be bypassed. There are two distinct cases:

 1. The target is a regular file (not a magic-link). Userspace depends
    on being able to re-open the O_PATH of a regular file, so we must
    define the mode to be a+rwx.

 2. The target is a magic-link. In this case, we simply copy the mode of
    the magic-link. This results in an O_PATH of a magic-link
    effectively acting as a no-op in terms of how much re-opening
    privileges a process has.

CAP_DAC_OVERRIDE can be used to override all of these restrictions, but
we only permit &init_userns's capabilities to affect these semantics.
The reason for this is that there isn't a clear way to track what
user_ns is the original owner of a given O_PATH chain -- thus an
unprivileged user could create a new userns and O_PATH the file
descriptor, owning it. All signs would indicate that the user really
does have CAP_DAC_OVERRIDE over the new descriptor and the protection
would be bypassed. We thus opt for the more conservative approach.

I have run this patch on several machines for several days. So far, the
only processes which have hit this case ("loadkeys" and "kbd_mode" from
the kbd package[1]) gracefully handle the permission error and do not
cause any user-visible problems. In order to give users a heads-up, a
warning is output to dmesg whenever may_open_magiclink() refuses access.

[1]: http://git.altlinux.org/people/legion/packages/kbd.git

Co-developed-by: Andy Lutomirski <luto@kernel.org>
Co-developed-by: Christian Brauner <christian@brauner.io>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 Documentation/filesystems/path-lookup.rst |  12 +--
 fs/internal.h                             |   1 +
 fs/namei.c                                | 105 +++++++++++++++++++---
 fs/open.c                                 |   3 +-
 fs/proc/fd.c                              |  23 ++++-
 include/linux/fs.h                        |   4 +
 include/linux/namei.h                     |   1 +
 7 files changed, 130 insertions(+), 19 deletions(-)

diff --git a/Documentation/filesystems/path-lookup.rst b/Documentation/filesystems/path-lookup.rst
index 434a07b0002b..a57d78ec8bee 100644
--- a/Documentation/filesystems/path-lookup.rst
+++ b/Documentation/filesystems/path-lookup.rst
@@ -1310,12 +1310,14 @@ longer needed.
 ``LOOKUP_JUMPED`` means that the current dentry was chosen not because
 it had the right name but for some other reason.  This happens when
 following "``..``", following a symlink to ``/``, crossing a mount point
-or accessing a "``/proc/$PID/fd/$FD``" symlink.  In this case the
-filesystem has not been asked to revalidate the name (with
-``d_revalidate()``).  In such cases the inode may still need to be
-revalidated, so ``d_op->d_weak_revalidate()`` is called if
+or accessing a "``/proc/$PID/fd/$FD``" symlink (also known as a "magic
+link"). In this case the filesystem has not been asked to revalidate the
+name (with ``d_revalidate()``).  In such cases the inode may still need
+to be revalidated, so ``d_op->d_weak_revalidate()`` is called if
 ``LOOKUP_JUMPED`` is set when the look completes - which may be at the
-final component or, when creating, unlinking, or renaming, at the penultimate component.
+final component or, when creating, unlinking, or renaming, at the
+penultimate component. ``LOOKUP_MAGICLINK_JUMPED`` is set alongside
+``LOOKUP_JUMPED`` if a magic-link was traversed.
 
 Final-component flags
 ~~~~~~~~~~~~~~~~~~~~~
diff --git a/fs/internal.h b/fs/internal.h
index a48ef81be37d..12847f502f49 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -119,6 +119,7 @@ struct open_flags {
 	int acc_mode;
 	int intent;
 	int lookup_flags;
+	fmode_t opath_mask;
 };
 extern struct file *do_filp_open(int dfd, struct filename *pathname,
 		const struct open_flags *op);
diff --git a/fs/namei.c b/fs/namei.c
index 20831c2fbb34..c6ba4ccafc51 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -872,7 +872,7 @@ void nd_jump_link(struct path *path)
 
 	nd->path = *path;
 	nd->inode = nd->path.dentry->d_inode;
-	nd->flags |= LOOKUP_JUMPED;
+	nd->flags |= LOOKUP_JUMPED | LOOKUP_MAGICLINK_JUMPED;
 }
 
 static inline void put_link(struct nameidata *nd)
@@ -1066,6 +1066,7 @@ const char *get_link(struct nameidata *nd)
 		return ERR_PTR(error);
 
 	nd->last_type = LAST_BIND;
+	nd->flags &= ~LOOKUP_MAGICLINK_JUMPED;
 	res = READ_ONCE(inode->i_link);
 	if (!res) {
 		const char * (*get)(struct dentry *, struct inode *,
@@ -3501,16 +3502,73 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
 	return error;
 }
 
-static int do_o_path(struct nameidata *nd, unsigned flags, struct file *file)
+/**
+ * may_reopen_magiclink - Check permissions for opening a trailing magic-link
+ * @opath_mask: the O_PATH mask of the magic-link
+ * @acc_mode: ACC_MODE which the user is attempting
+ *
+ * We block magic-link re-opening if the @opath_mask is more strict than the
+ * @acc_mode being requested, unless the user is capable(CAP_DAC_OVERRIDE).
+ *
+ * Returns 0 if successful, -ve on error.
+ */
+static int may_open_magiclink(fmode_t opath_mask, int acc_mode)
 {
-	struct path path;
-	int error = path_lookupat(nd, flags, &path);
-	if (!error) {
-		audit_inode(nd->name, path.dentry, 0);
-		error = vfs_open(&path, file);
-		path_put(&path);
-	}
-	return error;
+	/*
+	 * We only allow for init_userns to be able to override magic-links.
+	 * This is done to avoid cases where an unprivileged userns could take
+	 * an O_PATH of the fd, resulting in it being very unclear whether
+	 * CAP_DAC_OVERRIDE should work on the new O_PATH fd (given that it
+	 * pipes through to the underlying file).
+	 */
+	if (capable(CAP_DAC_OVERRIDE))
+		return 0;
+
+	if ((acc_mode & MAY_READ) &&
+	    !(opath_mask & (FMODE_READ | FMODE_PATH_READ)))
+		goto err;
+	if ((acc_mode & MAY_WRITE) &&
+	    !(opath_mask & (FMODE_WRITE | FMODE_PATH_WRITE)))
+		goto err;
+
+	return 0;
+
+err:
+	pr_warn_ratelimited("%s[%d]: magic-link re-open blocked (acc_mode=%s%s%s, opath_mask=%s%s%s%s)",
+		current->comm, task_pid_nr(current),
+		(acc_mode & MAY_READ) ? "r": "",
+		(acc_mode & MAY_WRITE) ? "w": "",
+		(acc_mode & MAY_EXEC) ? "x": "",
+		(opath_mask & FMODE_READ) ? "R" : "",
+		(opath_mask & FMODE_PATH_READ) ? "r" : "",
+		(opath_mask & FMODE_WRITE) ? "W" : "",
+		(opath_mask & FMODE_PATH_WRITE) ? "w" : "");
+	return -EACCES;
+}
+
+static int trailing_magiclink(struct nameidata *nd, int acc_mode,
+			      fmode_t *opath_mask)
+{
+	struct inode *inode = nd->link_inode;
+	fmode_t new_mask = 0;
+
+	/* Trailing symlink was not a magic-link. */
+	if (!(nd->flags & LOOKUP_MAGICLINK_JUMPED))
+		return 0;
+
+	/*
+	 * Figure out the O_PATH mask. Rather than using acl_permission_check,
+	 * we check whether any of the rw bits are set in the mode.
+	 */
+	if (inode->i_mode & S_IRUGO)
+		new_mask |= FMODE_PATH_READ;
+	if (inode->i_mode & S_IWUGO)
+		new_mask |= FMODE_PATH_WRITE;
+	if (opath_mask)
+		*opath_mask &= new_mask;
+
+	/* Is the new opath_mask more restrictive than acc_mode? */
+	return may_open_magiclink(new_mask, acc_mode);
 }
 
 static struct file *path_openat(struct nameidata *nd,
@@ -3526,13 +3584,38 @@ static struct file *path_openat(struct nameidata *nd,
 	if (unlikely(file->f_flags & __O_TMPFILE)) {
 		error = do_tmpfile(nd, flags, op, file);
 	} else if (unlikely(file->f_flags & O_PATH)) {
-		error = do_o_path(nd, flags, file);
+		/* Inlined path_lookupat() with a trailing_magiclink() check. */
+		const char *s = path_init(nd, flags);
+		fmode_t opath_mask = op->opath_mask;
+
+		while (!(error = link_path_walk(s, nd))
+			&& ((error = lookup_last(nd)) > 0)) {
+			s = trailing_symlink(nd);
+			error = trailing_magiclink(nd, op->acc_mode, &opath_mask);
+			if (error)
+				s = ERR_PTR(error);
+		}
+		if (!error)
+			error = complete_walk(nd);
+
+		if (!error && nd->flags & LOOKUP_DIRECTORY)
+			if (!d_can_lookup(nd->path.dentry))
+				error = -ENOTDIR;
+		if (!error) {
+			audit_inode(nd->name, nd->path.dentry, 0);
+			error = vfs_open(&nd->path, file);
+			file->f_mode |= opath_mask;
+		}
+		terminate_walk(nd);
 	} else {
 		const char *s = path_init(nd, flags);
 		while (!(error = link_path_walk(s, nd)) &&
 			(error = do_last(nd, file, op)) > 0) {
 			nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
 			s = trailing_symlink(nd);
+			error = trailing_magiclink(nd, op->acc_mode, NULL);
+			if (error)
+				s = ERR_PTR(error);
 		}
 		terminate_walk(nd);
 	}
diff --git a/fs/open.c b/fs/open.c
index b5b80469b93d..ab20eae39df7 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -982,8 +982,9 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 		acc_mode |= MAY_APPEND;
 
 	op->acc_mode = acc_mode;
-
 	op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
+	/* For O_PATH backwards-compatibility we default to an all-set mask. */
+	op->opath_mask = FMODE_PATH_READ | FMODE_PATH_WRITE;
 
 	if (flags & O_CREAT) {
 		op->intent |= LOOKUP_CREATE;
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 81882a13212d..9b7d8becb002 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -104,11 +104,30 @@ static void tid_fd_update_inode(struct task_struct *task, struct inode *inode,
 	task_dump_owner(task, 0, &inode->i_uid, &inode->i_gid);
 
 	if (S_ISLNK(inode->i_mode)) {
+		/*
+		 * Always set +x (depending on the fmode type), since there
+		 * currently aren't FMODE_PATH_EXEC restrictions and there is
+		 * no O_MAYEXEC yet. This might change in the future, in which
+		 * case we will restrict +x.
+		 */
 		unsigned i_mode = S_IFLNK;
+		if (f_mode & FMODE_PATH)
+			i_mode |= S_IXGRP;
+		else
+			i_mode |= S_IXUSR;
+		/*
+		 * Construct the mode bits based on the open-mode. The u+rwx
+		 * bits are for "ordinary" open modes while g+rwx are for
+		 * O_PATH modes.
+		 */
 		if (f_mode & FMODE_READ)
-			i_mode |= S_IRUSR | S_IXUSR;
+			i_mode |= S_IRUSR;
 		if (f_mode & FMODE_WRITE)
-			i_mode |= S_IWUSR | S_IXUSR;
+			i_mode |= S_IWUSR;
+		if (f_mode & FMODE_PATH_READ)
+			i_mode |= S_IRGRP;
+		if (f_mode & FMODE_PATH_WRITE)
+			i_mode |= S_IWGRP;
 		inode->i_mode = i_mode;
 	}
 	security_task_to_inode(task, inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..f7df213405ea 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -173,6 +173,10 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
 /* File does not contribute to nr_files count */
 #define FMODE_NOACCOUNT		((__force fmode_t)0x20000000)
 
+/* File is an O_PATH descriptor which can be upgraded to (read, write). */
+#define FMODE_PATH_READ		((__force fmode_t)0x40000000)
+#define FMODE_PATH_WRITE	((__force fmode_t)0x80000000)
+
 /*
  * Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
  * that indicates that they should check the contents of the iovec are
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 9138b4471dbf..bd6d3eb7764d 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -49,6 +49,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_ROOT		0x2000
 #define LOOKUP_EMPTY		0x4000
 #define LOOKUP_DOWN		0x8000
+#define LOOKUP_MAGICLINK_JUMPED	0x10000
 
 extern int path_pts(struct path *path);
 
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 2/9] procfs: switch magic-link modes to be more sane
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

Now that magic-link modes are obeyed for file re-opening purposes, some
of the pre-existing magic-link modes need to be adjusted to be more
semantically correct.

The most blatant example of this is /proc/self/exe, which had a mode of
a+rwx even though tautologically the file could never be opened for
writing (because it is the current->mm of a live process).

With the new O_PATH restrictions, changing the default mode of these
magic-links allows us to avoid delayed-access attacks such as we saw in
CVE-2019-5736.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 fs/proc/base.c       | 20 ++++++++++----------
 fs/proc/namespaces.c |  2 +-
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 255f6754c70d..82c06c21e69d 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -133,9 +133,9 @@ struct pid_entry {
 
 #define DIR(NAME, MODE, iops, fops)	\
 	NOD(NAME, (S_IFDIR|(MODE)), &iops, &fops, {} )
-#define LNK(NAME, get_link)					\
-	NOD(NAME, (S_IFLNK|S_IRWXUGO),				\
-		&proc_pid_link_inode_operations, NULL,		\
+#define LNK(NAME, MODE, get_link)			\
+	NOD(NAME, (S_IFLNK|(MODE)),			\
+		&proc_pid_link_inode_operations, NULL,	\
 		{ .proc_get_link = get_link } )
 #define REG(NAME, MODE, fops)				\
 	NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
@@ -2995,9 +2995,9 @@ static const struct pid_entry tgid_base_stuff[] = {
 	REG("numa_maps",  S_IRUGO, proc_pid_numa_maps_operations),
 #endif
 	REG("mem",        S_IRUSR|S_IWUSR, proc_mem_operations),
-	LNK("cwd",        proc_cwd_link),
-	LNK("root",       proc_root_link),
-	LNK("exe",        proc_exe_link),
+	LNK("cwd",        S_IRWXUGO, proc_cwd_link),
+	LNK("root",       S_IRWXUGO, proc_root_link),
+	LNK("exe",        S_IRUGO|S_IXUGO, proc_exe_link),
 	REG("mounts",     S_IRUGO, proc_mounts_operations),
 	REG("mountinfo",  S_IRUGO, proc_mountinfo_operations),
 	REG("mountstats", S_IRUSR, proc_mountstats_operations),
@@ -3393,11 +3393,11 @@ static const struct pid_entry tid_base_stuff[] = {
 	REG("numa_maps", S_IRUGO, proc_pid_numa_maps_operations),
 #endif
 	REG("mem",       S_IRUSR|S_IWUSR, proc_mem_operations),
-	LNK("cwd",       proc_cwd_link),
-	LNK("root",      proc_root_link),
-	LNK("exe",       proc_exe_link),
+	LNK("cwd",       S_IRWXUGO, proc_cwd_link),
+	LNK("root",      S_IRWXUGO, proc_root_link),
+	LNK("exe",       S_IRUGO|S_IXUGO, proc_exe_link),
 	REG("mounts",    S_IRUGO, proc_mounts_operations),
-	REG("mountinfo",  S_IRUGO, proc_mountinfo_operations),
+	REG("mountinfo", S_IRUGO, proc_mountinfo_operations),
 #ifdef CONFIG_PROC_PAGE_MONITOR
 	REG("clear_refs", S_IWUSR, proc_clear_refs_operations),
 	REG("smaps",     S_IRUGO, proc_pid_smaps_operations),
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index dd2b35f78b09..cd1e130913f7 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -94,7 +94,7 @@ static struct dentry *proc_ns_instantiate(struct dentry *dentry,
 	struct inode *inode;
 	struct proc_inode *ei;
 
-	inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK | S_IRWXUGO);
+	inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK | S_IRUGO);
 	if (!inode)
 		return ERR_PTR(-ENOENT);
 
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 3/9] open: O_EMPTYPATH: procfs-less file descriptor re-opening
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

Userspace has made use of /proc/self/fd very liberally to allow for
descriptors to be re-opened. There are a wide variety of uses for this
feature, but it has always required constructing a pathname and could
not be done without procfs mounted. The obvious solution for this is to
extend openat(2) to have an AT_EMPTY_PATH-equivalent -- O_EMPTYPATH.

Now that descriptor re-opening has been made safe through the new
magic-link resolution restrictions, we can replicate these restrictions
for O_EMPTYPATH. In particular, we only allow "upgrading" the file
descriptor if the corresponding FMODE_PATH_* bit is set (or the
FMODE_{READ,WRITE} cases for non-O_PATH file descriptors).

When doing openat(O_EMPTYPATH|O_PATH), O_PATH takes precedence and
O_EMPTYPATH is ignored. Very few users ever have a need to O_PATH
re-open an existing file descriptor, and so accommodating them at the
expense of further complicating O_PATH makes little sense. Ultimately,
if users ask for this we can always add RESOLVE_EMPTY_PATH to
resolveat(2) in the future.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 arch/alpha/include/uapi/asm/fcntl.h  |  1 +
 arch/parisc/include/uapi/asm/fcntl.h | 39 ++++++++++++++--------------
 arch/sparc/include/uapi/asm/fcntl.h  |  1 +
 fs/fcntl.c                           |  2 +-
 fs/namei.c                           | 20 ++++++++++++++
 fs/open.c                            |  7 ++++-
 include/linux/fcntl.h                |  2 +-
 include/uapi/asm-generic/fcntl.h     |  4 +++
 8 files changed, 54 insertions(+), 22 deletions(-)

diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h
index 50bdc8e8a271..1f879bade68b 100644
--- a/arch/alpha/include/uapi/asm/fcntl.h
+++ b/arch/alpha/include/uapi/asm/fcntl.h
@@ -34,6 +34,7 @@
 
 #define O_PATH		040000000
 #define __O_TMPFILE	0100000000
+#define O_EMPTYPATH	0200000000
 
 #define F_GETLK		7
 #define F_SETLK		8
diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h
index 03ce20e5ad7d..5d709058a76f 100644
--- a/arch/parisc/include/uapi/asm/fcntl.h
+++ b/arch/parisc/include/uapi/asm/fcntl.h
@@ -2,26 +2,27 @@
 #ifndef _PARISC_FCNTL_H
 #define _PARISC_FCNTL_H
 
-#define O_APPEND	000000010
-#define O_BLKSEEK	000000100 /* HPUX only */
-#define O_CREAT		000000400 /* not fcntl */
-#define O_EXCL		000002000 /* not fcntl */
-#define O_LARGEFILE	000004000
-#define __O_SYNC	000100000
+#define O_APPEND	0000000010
+#define O_BLKSEEK	0000000100 /* HPUX only */
+#define O_CREAT		0000000400 /* not fcntl */
+#define O_EXCL		0000002000 /* not fcntl */
+#define O_LARGEFILE	0000004000
+#define __O_SYNC	0000100000
 #define O_SYNC		(__O_SYNC|O_DSYNC)
-#define O_NONBLOCK	000200004 /* HPUX has separate NDELAY & NONBLOCK */
-#define O_NOCTTY	000400000 /* not fcntl */
-#define O_DSYNC		001000000 /* HPUX only */
-#define O_RSYNC		002000000 /* HPUX only */
-#define O_NOATIME	004000000
-#define O_CLOEXEC	010000000 /* set close_on_exec */
-
-#define O_DIRECTORY	000010000 /* must be a directory */
-#define O_NOFOLLOW	000000200 /* don't follow links */
-#define O_INVISIBLE	004000000 /* invisible I/O, for DMAPI/XDSM */
-
-#define O_PATH		020000000
-#define __O_TMPFILE	040000000
+#define O_NONBLOCK	0000200004 /* HPUX has separate NDELAY & NONBLOCK */
+#define O_NOCTTY	0000400000 /* not fcntl */
+#define O_DSYNC		0001000000 /* HPUX only */
+#define O_RSYNC		0002000000 /* HPUX only */
+#define O_NOATIME	0004000000
+#define O_CLOEXEC	0010000000 /* set close_on_exec */
+
+#define O_DIRECTORY	0000010000 /* must be a directory */
+#define O_NOFOLLOW	0000000200 /* don't follow links */
+#define O_INVISIBLE	0004000000 /* invisible I/O, for DMAPI/XDSM */
+
+#define O_PATH		0020000000
+#define __O_TMPFILE	0040000000
+#define O_EMPTYPATH	0100000000
 
 #define F_GETLK64	8
 #define F_SETLK64	9
diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h
index 67dae75e5274..dc86c9eaf950 100644
--- a/arch/sparc/include/uapi/asm/fcntl.h
+++ b/arch/sparc/include/uapi/asm/fcntl.h
@@ -37,6 +37,7 @@
 
 #define O_PATH		0x1000000
 #define __O_TMPFILE	0x2000000
+#define O_EMPTYPATH	0x4000000
 
 #define F_GETOWN	5	/*  for sockets. */
 #define F_SETOWN	6	/*  for sockets. */
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 3d40771e8e7c..4cf05a2fd162 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -1031,7 +1031,7 @@ static int __init fcntl_init(void)
 	 * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
 	 * is defined as O_NONBLOCK on some platforms and not on others.
 	 */
-	BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ !=
+	BUILD_BUG_ON(22 - 1 /* for O_RDONLY being 0 */ !=
 		HWEIGHT32(
 			(VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) |
 			__FMODE_EXEC | __FMODE_NONOTIFY));
diff --git a/fs/namei.c b/fs/namei.c
index c6ba4ccafc51..165861d621c2 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3571,6 +3571,24 @@ static int trailing_magiclink(struct nameidata *nd, int acc_mode,
 	return may_open_magiclink(new_mask, acc_mode);
 }
 
+static int do_emptypath(struct nameidata *nd, const struct open_flags *op,
+			struct file *file)
+{
+	int error;
+	/* We don't support AT_FDCWD (since O_PATH is disallowed here). */
+	struct fd f = fdget_raw(nd->dfd);
+
+	if (!f.file)
+		return -EBADF;
+
+	/* Apply trailing_magiclink()-like restrictions. */
+	error = may_open_magiclink(f.file->f_mode, op->acc_mode);
+	if (!error)
+		error = vfs_open(&f.file->f_path, file);
+	fdput(f);
+	return error;
+}
+
 static struct file *path_openat(struct nameidata *nd,
 			const struct open_flags *op, unsigned flags)
 {
@@ -3583,6 +3601,8 @@ static struct file *path_openat(struct nameidata *nd,
 
 	if (unlikely(file->f_flags & __O_TMPFILE)) {
 		error = do_tmpfile(nd, flags, op, file);
+	} else if (unlikely(file->f_flags & O_EMPTYPATH)) {
+		error = do_emptypath(nd, op, file);
 	} else if (unlikely(file->f_flags & O_PATH)) {
 		/* Inlined path_lookupat() with a trailing_magiclink() check. */
 		const char *s = path_init(nd, flags);
diff --git a/fs/open.c b/fs/open.c
index ab20eae39df7..bdca45528524 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -996,6 +996,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 		lookup_flags |= LOOKUP_DIRECTORY;
 	if (!(flags & O_NOFOLLOW))
 		lookup_flags |= LOOKUP_FOLLOW;
+	if (flags & O_EMPTYPATH)
+		lookup_flags |= LOOKUP_EMPTY;
 	op->lookup_flags = lookup_flags;
 	return 0;
 }
@@ -1057,14 +1059,17 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 {
 	struct open_flags op;
 	int fd = build_open_flags(flags, mode, &op);
+	int empty = 0;
 	struct filename *tmp;
 
 	if (fd)
 		return fd;
 
-	tmp = getname(filename);
+	tmp = getname_flags(filename, op.lookup_flags, &empty);
 	if (IS_ERR(tmp))
 		return PTR_ERR(tmp);
+	if (!empty)
+		op.open_flag &= ~O_EMPTYPATH;
 
 	fd = get_unused_fd_flags(flags);
 	if (fd >= 0) {
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index d019df946cb2..2868ae6c8fc1 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -9,7 +9,7 @@
 	(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
 	 O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | __O_SYNC | O_DSYNC | \
 	 FASYNC	| O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
-	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE)
+	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTYPATH)
 
 #ifndef force_o_largefile
 #define force_o_largefile() (!IS_ENABLED(CONFIG_ARCH_32BIT_OFF_T))
diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h
index 9dc0bf0c5a6e..ae6862f69cc2 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -89,6 +89,10 @@
 #define __O_TMPFILE	020000000
 #endif
 
+#ifndef O_EMPTYPATH
+#define O_EMPTYPATH 040000000
+#endif
+
 /* a horrid kludge trying to make sure that this will fail on old kernels */
 #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
 #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT)      
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 4/9] namei: O_BENEATH-style path resolution flags
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

Add the following flags to allow various restrictions on path resolution
(these affect the *entire* resolution, rather than just the final path
component -- as is the case with LOOKUP_FOLLOW).

The primary justification for these flags is to allow for programs to be
far more strict about how they want path resolution to handle symlinks,
mountpoint crossings, and paths that escape the dirfd (through an
absolute path or ".." shenanigans).

This is of particular concern to container runtimes that want to be very
careful about malicious root filesystems that a container's init might
have screwed around with (and there is no real way to protect against
this in userspace if you consider potential races against a malicious
container's init). More classical applications (which have their own
potentially buggy userspace path sanitisation code) include web servers,
archive extraction tools, network file servers, and so on.

These flags are exposed to userspace through openat2(2) in a later
patchset.

* LOOKUP_NO_XDEV: Disallow mount-point crossing (both *down* into one,
  or *up* from one). Both bind-mounts and cross-filesystem mounts are
  blocked by this flag. The naming is based on "find -xdev" as well as
  -EXDEV (though find(1) doesn't walk upwards, the semantics seem
  obvious).

* LOOKUP_NO_MAGICLINKS: Disallows ->get_link "symlink" (or rather,
  magic-link) jumping. This is a very specific restriction, and it
  exists because /proc/$pid/fd/... "symlinks" allow for access outside
  nd->root and pose risk to container runtimes that don't want to be
  tricked into accessing a host path (but do want to allow
  no-funny-business symlink resolution).

* LOOKUP_NO_SYMLINKS: Disallows resolution through symlinks of any kind
  (including magic-links).

* LOOKUP_BENEATH: Disallow "escapes" from the starting point of the
  filesystem tree during resolution (you must stay "beneath" the
  starting point at all times). Currently this is done by disallowing
  ".." and absolute paths (either in the given path or found during
  symlink resolution) entirely, as well as all magic-link jumping.

  The wholesale banning of ".." is because it is currently not safe to
  allow ".." resolution (races can cause the path to be moved outside of
  the root -- this is conceptually similar to historical chroot(2)
  escape attacks). Future patches in this series will address this, and
  will re-enable ".." resolution once it is safe. With those patches,
  ".." resolution will only be allowed if it remains in the root
  throughout resolution (such as "a/../b" not "a/../../outside/b").

  The banning of magic-link jumping is done because it is not clear
  whether semantically they should be allowed -- while some magic-links
  are safe there are many that can cause escapes (and once a
  resolution is outside of the root, O_BENEATH will no longer detect
  it). Future patches may re-enable magic-link jumping when such jumps
  would remain inside the root.

The LOOKUP_NO_*LINK flags return -ELOOP if path resolution would
violates their requirement, while the others all return -EXDEV.

This is a refresh of Al's AT_NO_JUMPS patchset[1] (which was a variation
on David Drysdale's O_BENEATH patchset[2], which in turn was based on
the Capsicum project[3]). Input from Linus and Andy in the AT_NO_JUMPS
thread[4] determined most of the API changes made in this refresh.

[1]: https://lwn.net/Articles/721443/
[2]: https://lwn.net/Articles/619151/
[3]: https://lwn.net/Articles/603929/
[4]: https://lwn.net/Articles/723057/

Cc: Christian Brauner <christian@brauner.io>
Suggested-by: David Drysdale <drysdale@google.com>
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Suggested-by: Andy Lutomirski <luto@kernel.org>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 fs/namei.c            | 85 ++++++++++++++++++++++++++++++++++++-------
 include/linux/namei.h |  7 ++++
 2 files changed, 78 insertions(+), 14 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 165861d621c2..9df4aa35aedc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -674,7 +674,11 @@ static int unlazy_walk(struct nameidata *nd)
 		goto out2;
 	if (unlikely(!legitimize_path(nd, &nd->path, nd->seq)))
 		goto out1;
-	if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
+	if (!nd->root.mnt) {
+		/* Restart from path_init() if nd->root was cleared. */
+		if (nd->flags & LOOKUP_BENEATH)
+			goto out;
+	} else if (!(nd->flags & LOOKUP_ROOT)) {
 		if (unlikely(!legitimize_path(nd, &nd->root, nd->root_seq)))
 			goto out;
 	}
@@ -843,6 +847,13 @@ static inline void path_to_nameidata(const struct path *path,
 
 static int nd_jump_root(struct nameidata *nd)
 {
+	if (unlikely(nd->flags & LOOKUP_BENEATH))
+		return -EXDEV;
+	if (unlikely(nd->flags & LOOKUP_NO_XDEV)) {
+		/* Absolute path arguments to path_init() are allowed. */
+		if (nd->path.mnt != NULL && nd->path.mnt != nd->root.mnt)
+			return -EXDEV;
+	}
 	if (nd->flags & LOOKUP_RCU) {
 		struct dentry *d;
 		nd->path = nd->root;
@@ -1051,6 +1062,9 @@ const char *get_link(struct nameidata *nd)
 	int error;
 	const char *res;
 
+	if (unlikely(nd->flags & LOOKUP_NO_SYMLINKS))
+		return ERR_PTR(-ELOOP);
+
 	if (!(nd->flags & LOOKUP_RCU)) {
 		touch_atime(&last->link);
 		cond_resched();
@@ -1082,14 +1096,22 @@ const char *get_link(struct nameidata *nd)
 		} else {
 			res = get(dentry, inode, &last->done);
 		}
+		if (nd->flags & LOOKUP_MAGICLINK_JUMPED) {
+			if (unlikely(nd->flags & LOOKUP_NO_MAGICLINKS))
+				return ERR_PTR(-ELOOP);
+			/* Not currently safe. */
+			if (unlikely(nd->flags & LOOKUP_BENEATH))
+				return ERR_PTR(-EXDEV);
+		}
 		if (IS_ERR_OR_NULL(res))
 			return res;
 	}
 	if (*res == '/') {
 		if (!nd->root.mnt)
 			set_root(nd);
-		if (unlikely(nd_jump_root(nd)))
-			return ERR_PTR(-ECHILD);
+		error = nd_jump_root(nd);
+		if (unlikely(error))
+			return ERR_PTR(error);
 		while (unlikely(*++res == '/'))
 			;
 	}
@@ -1270,12 +1292,16 @@ static int follow_managed(struct path *path, struct nameidata *nd)
 		break;
 	}
 
-	if (need_mntput && path->mnt == mnt)
-		mntput(path->mnt);
+	if (need_mntput) {
+		if (path->mnt == mnt)
+			mntput(path->mnt);
+		if (unlikely(nd->flags & LOOKUP_NO_XDEV))
+			ret = -EXDEV;
+		else
+			nd->flags |= LOOKUP_JUMPED;
+	}
 	if (ret == -EISDIR || !ret)
 		ret = 1;
-	if (need_mntput)
-		nd->flags |= LOOKUP_JUMPED;
 	if (unlikely(ret < 0))
 		path_put_conditional(path, nd);
 	return ret;
@@ -1332,6 +1358,8 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
 		mounted = __lookup_mnt(path->mnt, path->dentry);
 		if (!mounted)
 			break;
+		if (unlikely(nd->flags & LOOKUP_NO_XDEV))
+			return false;
 		path->mnt = &mounted->mnt;
 		path->dentry = mounted->mnt.mnt_root;
 		nd->flags |= LOOKUP_JUMPED;
@@ -1352,8 +1380,11 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 	struct inode *inode = nd->inode;
 
 	while (1) {
-		if (path_equal(&nd->path, &nd->root))
+		if (path_equal(&nd->path, &nd->root)) {
+			if (unlikely(nd->flags & LOOKUP_BENEATH))
+				return -EXDEV;
 			break;
+		}
 		if (nd->path.dentry != nd->path.mnt->mnt_root) {
 			struct dentry *old = nd->path.dentry;
 			struct dentry *parent = old->d_parent;
@@ -1378,6 +1409,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 				return -ECHILD;
 			if (&mparent->mnt == nd->path.mnt)
 				break;
+			if (unlikely(nd->flags & LOOKUP_NO_XDEV))
+				return -EXDEV;
 			/* we know that mountpoint was pinned */
 			nd->path.dentry = mountpoint;
 			nd->path.mnt = &mparent->mnt;
@@ -1392,6 +1425,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 			return -ECHILD;
 		if (!mounted)
 			break;
+		if (unlikely(nd->flags & LOOKUP_NO_XDEV))
+			return -EXDEV;
 		nd->path.mnt = &mounted->mnt;
 		nd->path.dentry = mounted->mnt.mnt_root;
 		inode = nd->path.dentry->d_inode;
@@ -1480,8 +1515,11 @@ static int path_parent_directory(struct path *path)
 static int follow_dotdot(struct nameidata *nd)
 {
 	while(1) {
-		if (path_equal(&nd->path, &nd->root))
+		if (path_equal(&nd->path, &nd->root)) {
+			if (unlikely(nd->flags & LOOKUP_BENEATH))
+				return -EXDEV;
 			break;
+		}
 		if (nd->path.dentry != nd->path.mnt->mnt_root) {
 			int ret = path_parent_directory(&nd->path);
 			if (ret)
@@ -1490,6 +1528,8 @@ static int follow_dotdot(struct nameidata *nd)
 		}
 		if (!follow_up(&nd->path))
 			break;
+		if (unlikely(nd->flags & LOOKUP_NO_XDEV))
+			return -EXDEV;
 	}
 	follow_mount(&nd->path);
 	nd->inode = nd->path.dentry->d_inode;
@@ -1704,6 +1744,13 @@ static inline int may_lookup(struct nameidata *nd)
 static inline int handle_dots(struct nameidata *nd, int type)
 {
 	if (type == LAST_DOTDOT) {
+		/*
+		 * LOOKUP_BENEATH resolving ".." is not currently safe -- races can
+		 * cause our parent to have moved outside of the root and us to skip
+		 * over it.
+		 */
+		if (unlikely(nd->flags & LOOKUP_BENEATH))
+			return -EXDEV;
 		if (!nd->root.mnt)
 			set_root(nd);
 		if (nd->flags & LOOKUP_RCU) {
@@ -2170,6 +2217,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 /* must be paired with terminate_walk() */
 static const char *path_init(struct nameidata *nd, unsigned flags)
 {
+	int error;
 	const char *s = nd->name->name;
 
 	if (!*s)
@@ -2202,11 +2250,13 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 	nd->path.dentry = NULL;
 
 	nd->m_seq = read_seqbegin(&mount_lock);
+
+	/* Figure out the starting path and root (if needed). */
 	if (*s == '/') {
 		set_root(nd);
-		if (likely(!nd_jump_root(nd)))
-			return s;
-		return ERR_PTR(-ECHILD);
+		error = nd_jump_root(nd);
+		if (unlikely(error))
+			return ERR_PTR(error);
 	} else if (nd->dfd == AT_FDCWD) {
 		if (flags & LOOKUP_RCU) {
 			struct fs_struct *fs = current->fs;
@@ -2222,7 +2272,6 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 			get_fs_pwd(current->fs, &nd->path);
 			nd->inode = nd->path.dentry->d_inode;
 		}
-		return s;
 	} else {
 		/* Caller must check execute permissions on the starting path component */
 		struct fd f = fdget_raw(nd->dfd);
@@ -2247,8 +2296,16 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 			nd->inode = nd->path.dentry->d_inode;
 		}
 		fdput(f);
-		return s;
 	}
+	/* For scoped-lookups we need to set the root to the dirfd as well. */
+	if (flags & LOOKUP_BENEATH) {
+		nd->root = nd->path;
+		if (flags & LOOKUP_RCU)
+			nd->root_seq = nd->seq;
+		else
+			path_get(&nd->root);
+	}
+	return s;
 }
 
 static const char *trailing_symlink(struct nameidata *nd)
diff --git a/include/linux/namei.h b/include/linux/namei.h
index bd6d3eb7764d..be407415c28a 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -51,6 +51,13 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_DOWN		0x8000
 #define LOOKUP_MAGICLINK_JUMPED	0x10000
 
+/* Scoping flags for lookup. */
+#define LOOKUP_BENEATH		0x020000 /* No escaping from starting point. */
+#define LOOKUP_NO_XDEV		0x040000 /* No mountpoint crossing. */
+#define LOOKUP_NO_MAGICLINKS	0x080000 /* No /proc/$pid/fd/ "symlink" crossing. */
+#define LOOKUP_NO_SYMLINKS	0x100000 /* No symlink crossing *at all*.
+					    Implies LOOKUP_NO_MAGICLINKS. */
+
 extern int path_pts(struct path *path);
 
 extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 5/9] namei: LOOKUP_IN_ROOT: chroot-like path resolution
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

The primary motivation for the need for this flag is container runtimes
which have to interact with malicious root filesystems in the host
namespaces. One of the first requirements for a container runtime to be
secure against a malicious rootfs is that they correctly scope symlinks
(that is, they should be scoped as though they are chroot(2)ed into the
container's rootfs) and ".."-style paths[*]. The already-existing
LOOKUP_NO_XDEV and LOOKUP_NO_MAGICLINKS help defend against other
potential attacks in a malicious rootfs scenario.

Currently most container runtimes try to do this resolution in
userspace[1], causing many potential race conditions. In addition, the
"obvious" alternative (actually performing a {ch,pivot_}root(2))
requires a fork+exec (for some runtimes) which is *very* costly if
necessary for every filesystem operation involving a container.

[*] At the moment, ".." and magic-link jumping are disallowed for the
    same reason it is disabled for LOOKUP_BENEATH -- currently it is not
    safe to allow it. Future patches may enable it unconditionally once
    we have resolved the possible races (for "..") and semantics (for
    magic-link jumping).

The most significant *at(2) semantic change with LOOKUP_IN_ROOT is that
absolute pathnames no longer cause the dirfd to be ignored completely.

The rationale is that LOOKUP_IN_ROOT must necessarily chroot-scope
symlinks with absolute paths to dirfd, and so doing it for the base path
seems to be the most consistent behaviour (and also avoids foot-gunning
users who want to scope paths that are absolute).

[1]: https://github.com/cyphar/filepath-securejoin

Co-developed-by: Christian Brauner <christian@brauner.io>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 fs/namei.c            | 41 +++++++++++++++++++++++++++++++----------
 include/linux/namei.h |  1 +
 2 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 9df4aa35aedc..617fc7d55977 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -676,7 +676,7 @@ static int unlazy_walk(struct nameidata *nd)
 		goto out1;
 	if (!nd->root.mnt) {
 		/* Restart from path_init() if nd->root was cleared. */
-		if (nd->flags & LOOKUP_BENEATH)
+		if (nd->flags & (LOOKUP_BENEATH | LOOKUP_IN_ROOT))
 			goto out;
 	} else if (!(nd->flags & LOOKUP_ROOT)) {
 		if (unlikely(!legitimize_path(nd, &nd->root, nd->root_seq)))
@@ -809,10 +809,18 @@ static int complete_walk(struct nameidata *nd)
 	return status;
 }
 
-static void set_root(struct nameidata *nd)
+static int set_root(struct nameidata *nd)
 {
 	struct fs_struct *fs = current->fs;
 
+	/*
+	 * Jumping to the real root as part of LOOKUP_IN_ROOT is a BUG in namei,
+	 * but we still have to ensure it doesn't happen because it will cause a
+	 * breakout from the dirfd.
+	 */
+	if (WARN_ON(nd->flags & LOOKUP_IN_ROOT))
+		return -ENOTRECOVERABLE;
+
 	if (nd->flags & LOOKUP_RCU) {
 		unsigned seq;
 
@@ -824,6 +832,7 @@ static void set_root(struct nameidata *nd)
 	} else {
 		get_fs_root(fs, &nd->root);
 	}
+	return 0;
 }
 
 static void path_put_conditional(struct path *path, struct nameidata *nd)
@@ -854,6 +863,11 @@ static int nd_jump_root(struct nameidata *nd)
 		if (nd->path.mnt != NULL && nd->path.mnt != nd->root.mnt)
 			return -EXDEV;
 	}
+	if (!nd->root.mnt) {
+		int error = set_root(nd);
+		if (error)
+			return error;
+	}
 	if (nd->flags & LOOKUP_RCU) {
 		struct dentry *d;
 		nd->path = nd->root;
@@ -1100,15 +1114,13 @@ const char *get_link(struct nameidata *nd)
 			if (unlikely(nd->flags & LOOKUP_NO_MAGICLINKS))
 				return ERR_PTR(-ELOOP);
 			/* Not currently safe. */
-			if (unlikely(nd->flags & LOOKUP_BENEATH))
+			if (unlikely(nd->flags & (LOOKUP_BENEATH | LOOKUP_IN_ROOT)))
 				return ERR_PTR(-EXDEV);
 		}
 		if (IS_ERR_OR_NULL(res))
 			return res;
 	}
 	if (*res == '/') {
-		if (!nd->root.mnt)
-			set_root(nd);
 		error = nd_jump_root(nd);
 		if (unlikely(error))
 			return ERR_PTR(error);
@@ -1744,15 +1756,20 @@ static inline int may_lookup(struct nameidata *nd)
 static inline int handle_dots(struct nameidata *nd, int type)
 {
 	if (type == LAST_DOTDOT) {
+		int error = 0;
+
 		/*
 		 * LOOKUP_BENEATH resolving ".." is not currently safe -- races can
 		 * cause our parent to have moved outside of the root and us to skip
 		 * over it.
 		 */
-		if (unlikely(nd->flags & LOOKUP_BENEATH))
+		if (unlikely(nd->flags & (LOOKUP_BENEATH | LOOKUP_IN_ROOT)))
 			return -EXDEV;
-		if (!nd->root.mnt)
-			set_root(nd);
+		if (!nd->root.mnt) {
+			error = set_root(nd);
+			if (error)
+				return error;
+		}
 		if (nd->flags & LOOKUP_RCU) {
 			return follow_dotdot_rcu(nd);
 		} else
@@ -2251,9 +2268,13 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 
 	nd->m_seq = read_seqbegin(&mount_lock);
 
+	/* LOOKUP_IN_ROOT treats absolute paths as being relative-to-dirfd. */
+	if (flags & LOOKUP_IN_ROOT)
+		while (*s == '/')
+			s++;
+
 	/* Figure out the starting path and root (if needed). */
 	if (*s == '/') {
-		set_root(nd);
 		error = nd_jump_root(nd);
 		if (unlikely(error))
 			return ERR_PTR(error);
@@ -2298,7 +2319,7 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 		fdput(f);
 	}
 	/* For scoped-lookups we need to set the root to the dirfd as well. */
-	if (flags & LOOKUP_BENEATH) {
+	if (flags & (LOOKUP_BENEATH | LOOKUP_IN_ROOT)) {
 		nd->root = nd->path;
 		if (flags & LOOKUP_RCU)
 			nd->root_seq = nd->seq;
diff --git a/include/linux/namei.h b/include/linux/namei.h
index be407415c28a..ec2c6c588ea7 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -57,6 +57,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_NO_MAGICLINKS	0x080000 /* No /proc/$pid/fd/ "symlink" crossing. */
 #define LOOKUP_NO_SYMLINKS	0x100000 /* No symlink crossing *at all*.
 					    Implies LOOKUP_NO_MAGICLINKS. */
+#define LOOKUP_IN_ROOT		0x200000 /* Treat dirfd as %current->fs->root. */
 
 extern int path_pts(struct path *path);
 
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 6/9] namei: aggressively check for nd->root escape on ".." resolution
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

This patch allows for LOOKUP_BENEATH and LOOKUP_IN_ROOT to safely permit
".." resolution (in the case of LOOKUP_BENEATH the resolution will still
fail if ".." resolution would resolve a path outside of the root --
while LOOKUP_IN_ROOT will chroot(2)-style scope it). Magic-link jumps
are still disallowed entirely because now they could result in
inconsistent behaviour if resolution encounters a subsequent ".."[*].

The need for this patch is explained by observing there is a fairly
easy-to-exploit race condition with chroot(2) (and thus by extension
LOOKUP_IN_ROOT and LOOKUP_BENEATH if ".." is allowed) where a rename(2)
of a path can be used to "skip over" nd->root and thus escape to the
filesystem above nd->root.

  thread1 [attacker]:
    for (;;)
      renameat2(AT_FDCWD, "/a/b/c", AT_FDCWD, "/a/d", RENAME_EXCHANGE);
  thread2 [victim]:
    for (;;)
      resolveat(dirb, "b/c/../../etc/shadow", RESOLVE_IN_ROOT);

With fairly significant regularity, thread2 will resolve to
"/etc/shadow" rather than "/a/b/etc/shadow". There is also a similar
(though somewhat more privileged) attack using MS_MOVE.

With this patch, such cases will be detected *during* ".." resolution
(which is the weak point of chroot(2) -- since walking *into* a
subdirectory tautologically cannot result in you walking *outside*
nd->root -- except through a bind-mount or magic-link). By detecting
this at ".." resolution (rather than checking only at the end of the
entire resolution) we can both correct escapes by jumping back to the
root (in the case of LOOKUP_IN_ROOT), as well as avoid revealing to
attackers the structure of the filesystem outside of the root (through
timing attacks for instance).

In order to avoid a quadratic lookup with each ".." entry, we only
activate the slow path if a write through &rename_lock or &mount_lock
has occurred during path resolution (&rename_lock and &mount_lock are
re-taken to further optimise the lookup). Since the primary attack being
protected against is MS_MOVE or rename(2), not doing additional checks
unless a mount or rename have occurred avoids making the common case
slow.

The use of path_is_under() here might seem suspect, but on further
inspection of the most important race (a path was *inside* the root but
is now *outside*), there appears to be no attack potential:

  * If path_is_under() occurs before the rename, then the path will be
    resolved -- however the path was originally inside the root and thus
    there is no escape (and to userspace it'd look like the rename
    occurred after the path was resolved). If path_is_under() occurs
    afterwards, the resolution is blocked.

  * Subsequent ".." jumps are guaranteed to check path_is_under() -- by
    construction, &rename_lock or &mount_lock must have been taken by
    the attacker after path_is_under() returned in the victim. Thus ".."
    will not be able to escape from the previously-inside-root path.

  * Walking down in the moved path is still safe since the entire
    subtree was moved (either by rename(2) or MS_MOVE) and because (as
    discussed above) walking down is safe.

A variant of the above attack is included in the selftests for
openat2(2) later in this patch series. I've run this test on several
machines for several days and no instances of a breakout were detected.
While this is not concrete proof that this is safe, when combined with
the above argument it should lend some trustworthiness to this
construction.

[*] It may be acceptable in the future to do a path_is_under() check
    after resolving a magic-link and permit resolution if the
    nd_jump_link() result is still within the dirfd. However this seems
    unlikely to be a feature that people *really* need* -- it can be
    added later if it turns out a lot of people want it.

Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Jann Horn <jannh@google.com>
Cc: Kees Cook <keescook@chromium.org>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 fs/namei.c | 45 +++++++++++++++++++++++++++++++--------------
 1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 617fc7d55977..b3abf5754ddd 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -491,7 +491,7 @@ struct nameidata {
 	struct path	root;
 	struct inode	*inode; /* path.dentry.d_inode */
 	unsigned int	flags;
-	unsigned	seq, m_seq;
+	unsigned	seq, m_seq, r_seq;
 	int		last_type;
 	unsigned	depth;
 	int		total_link_count;
@@ -1758,22 +1758,36 @@ static inline int handle_dots(struct nameidata *nd, int type)
 	if (type == LAST_DOTDOT) {
 		int error = 0;
 
-		/*
-		 * LOOKUP_BENEATH resolving ".." is not currently safe -- races can
-		 * cause our parent to have moved outside of the root and us to skip
-		 * over it.
-		 */
-		if (unlikely(nd->flags & (LOOKUP_BENEATH | LOOKUP_IN_ROOT)))
-			return -EXDEV;
 		if (!nd->root.mnt) {
 			error = set_root(nd);
 			if (error)
 				return error;
 		}
-		if (nd->flags & LOOKUP_RCU) {
-			return follow_dotdot_rcu(nd);
-		} else
-			return follow_dotdot(nd);
+		if (nd->flags & LOOKUP_RCU)
+			error = follow_dotdot_rcu(nd);
+		else
+			error = follow_dotdot(nd);
+		if (error)
+			return error;
+
+		if (unlikely(nd->flags & (LOOKUP_BENEATH | LOOKUP_IN_ROOT))) {
+			bool m_retry = read_seqretry(&mount_lock, nd->m_seq);
+			bool r_retry = read_seqretry(&rename_lock, nd->r_seq);
+
+			/*
+			 * Don't bother checking unless there's a racing
+			 * rename(2) or MS_MOVE.
+			 */
+			if (likely(!m_retry && !r_retry))
+				return 0;
+
+			if (m_retry && !(nd->flags & LOOKUP_RCU))
+				nd->m_seq = read_seqbegin(&mount_lock);
+			if (r_retry)
+				nd->r_seq = read_seqbegin(&rename_lock);
+			if (!path_is_under(&nd->path, &nd->root))
+				return -EXDEV;
+		}
 	}
 	return 0;
 }
@@ -2245,6 +2259,11 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 	nd->last_type = LAST_ROOT; /* if there are only slashes... */
 	nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
 	nd->depth = 0;
+
+	nd->m_seq = read_seqbegin(&mount_lock);
+	if (flags & (LOOKUP_BENEATH | LOOKUP_IN_ROOT))
+		nd->r_seq = read_seqbegin(&rename_lock);
+
 	if (flags & LOOKUP_ROOT) {
 		struct dentry *root = nd->root.dentry;
 		struct inode *inode = root->d_inode;
@@ -2266,8 +2285,6 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 	nd->path.mnt = NULL;
 	nd->path.dentry = NULL;
 
-	nd->m_seq = read_seqbegin(&mount_lock);
-
 	/* LOOKUP_IN_ROOT treats absolute paths as being relative-to-dirfd. */
 	if (flags & LOOKUP_IN_ROOT)
 		while (*s == '/')
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 7/9] open: openat2(2) syscall
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

The most obvious syscall to add support for the new LOOKUP_* scoping
flags would be openat(2). However, there are a few reasons why this is
not the best course of action:

 * The new LOOKUP_* flags are intended to be security features, and
   openat(2) will silently ignore all unknown flags. This means that
   users would need to avoid foot-gunning themselves constantly when
   using this interface if it were part of openat(2). This can be fixed
   by having userspace libraries handle this for users[1], but should be
   avoided if possible.

 * Resolution scoping feels like a different operation to the existing
   O_* flags. And since openat(2) has limited flag space, it seems to be
   quite wasteful to clutter it with 5 flags that are all
   resolution-related. Arguably O_NOFOLLOW is also a resolution flag but
   its entire purpose is to error out if you encounter a trailing
   symlink -- not to scope resolution.

 * Other systems would be able to reimplement this syscall allowing for
   cross-OS standardisation rather than being hidden amongst O_* flags
   which may result in it not being used by all the parties that might
   want to use it (file servers, web servers, container runtimes, etc).

 * It gives us the opportunity to iterate on the O_PATH interface. In
   particular, the new @how->upgrade_mask field for fd re-opening is
   only possible because we have a clean slate without needing to re-use
   the ACC_MODE flag design nor the existing openat(2) @mode semantics.

To this end, we introduce the openat2(2) syscall. It provides all of the
features of openat(2) through the @how->flags argument, but also
also provides a new @how->resolve argument which exposes RESOLVE_* flags
that map to our new LOOKUP_* flags. It also eliminates the long-standing
ugliness of variadic-open(2) by embedding it in a struct.

In order to allow for userspace to lock down their usage of file
descriptor re-opening, openat2(2) has the ability for users to disallow
certain re-opening modes through @how->upgrade_mask. At the moment,
there is no UPGRADE_NOEXEC. The open_how struct is padded to 64 bytes
for future extensions (all of the reserved bits must be zeroed).

[1]: https://github.com/openSUSE/libpathrs

Co-developed-by: Christian Brauner <christian@brauner.io>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 arch/alpha/kernel/syscalls/syscall.tbl      |   1 +
 arch/arm/tools/syscall.tbl                  |   1 +
 arch/arm64/include/asm/unistd.h             |   2 +-
 arch/arm64/include/asm/unistd32.h           |   2 +
 arch/ia64/kernel/syscalls/syscall.tbl       |   1 +
 arch/m68k/kernel/syscalls/syscall.tbl       |   1 +
 arch/microblaze/kernel/syscalls/syscall.tbl |   1 +
 arch/mips/kernel/syscalls/syscall_n32.tbl   |   1 +
 arch/mips/kernel/syscalls/syscall_n64.tbl   |   1 +
 arch/mips/kernel/syscalls/syscall_o32.tbl   |   1 +
 arch/parisc/kernel/syscalls/syscall.tbl     |   1 +
 arch/powerpc/kernel/syscalls/syscall.tbl    |   1 +
 arch/s390/kernel/syscalls/syscall.tbl       |   1 +
 arch/sh/kernel/syscalls/syscall.tbl         |   1 +
 arch/sparc/kernel/syscalls/syscall.tbl      |   1 +
 arch/x86/entry/syscalls/syscall_32.tbl      |   1 +
 arch/x86/entry/syscalls/syscall_64.tbl      |   1 +
 arch/xtensa/kernel/syscalls/syscall.tbl     |   1 +
 fs/open.c                                   | 106 ++++++++++++++++----
 include/linux/fcntl.h                       |  15 ++-
 include/linux/fs.h                          |   4 +-
 include/linux/syscalls.h                    |  17 +++-
 include/uapi/asm-generic/unistd.h           |   4 +-
 include/uapi/linux/fcntl.h                  |  42 ++++++++
 24 files changed, 178 insertions(+), 30 deletions(-)

diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 9e7704e44f6d..cebe813a947f 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -473,3 +473,4 @@
 541	common	fsconfig			sys_fsconfig
 542	common	fsmount				sys_fsmount
 543	common	fspick				sys_fspick
+547	common	openat2				sys_openat2
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index aaf479a9e92d..2a0b94e595e7 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -447,3 +447,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h
index c9f8dd421c5f..40b8fec7ba55 100644
--- a/arch/arm64/include/asm/unistd.h
+++ b/arch/arm64/include/asm/unistd.h
@@ -33,7 +33,7 @@
 #define __ARM_NR_compat_set_tls		(__ARM_NR_COMPAT_BASE + 5)
 #define __ARM_NR_COMPAT_END		(__ARM_NR_COMPAT_BASE + 0x800)
 
-#define __NR_compat_syscalls		434
+#define __NR_compat_syscalls		438
 #endif
 
 #define __ARCH_WANT_SYS_CLONE
diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h
index aa995920bd34..df60d417832e 100644
--- a/arch/arm64/include/asm/unistd32.h
+++ b/arch/arm64/include/asm/unistd32.h
@@ -875,6 +875,8 @@ __SYSCALL(__NR_fsconfig, sys_fsconfig)
 __SYSCALL(__NR_fsmount, sys_fsmount)
 #define __NR_fspick 433
 __SYSCALL(__NR_fspick, sys_fspick)
+#define __NR_openat2 437
+__SYSCALL(__NR_openat2, sys_openat2)
 
 /*
  * Please add new compat syscalls above this comment and update
diff --git a/arch/ia64/kernel/syscalls/syscall.tbl b/arch/ia64/kernel/syscalls/syscall.tbl
index e01df3f2f80d..4da37f0f17fc 100644
--- a/arch/ia64/kernel/syscalls/syscall.tbl
+++ b/arch/ia64/kernel/syscalls/syscall.tbl
@@ -354,3 +354,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 7e3d0734b2f3..323a3695c8a3 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -433,3 +433,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 26339e417695..8a5b81663387 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -439,3 +439,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index 0e2dd68ade57..70dfc63d160d 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -372,3 +372,4 @@
 431	n32	fsconfig			sys_fsconfig
 432	n32	fsmount				sys_fsmount
 433	n32	fspick				sys_fspick
+437	n32	openat2				sys_openat2
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 5eebfa0d155c..1fcad88a326f 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -348,3 +348,4 @@
 431	n64	fsconfig			sys_fsconfig
 432	n64	fsmount				sys_fsmount
 433	n64	fspick				sys_fspick
+437	n64	openat2				sys_openat2
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 3cc1374e02d0..5ef8bdce49ca 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -421,3 +421,4 @@
 431	o32	fsconfig			sys_fsconfig
 432	o32	fsmount				sys_fsmount
 433	o32	fspick				sys_fspick
+437	o32	openat2				sys_openat2			sys_openat2
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index c9e377d59232..176de3591738 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -430,3 +430,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 103655d84b4b..59591311f8e2 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -515,3 +515,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index e822b2964a83..36ca509e26c2 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -436,3 +436,4 @@
 431  common	fsconfig		sys_fsconfig			sys_fsconfig
 432  common	fsmount			sys_fsmount			sys_fsmount
 433  common	fspick			sys_fspick			sys_fspick
+437  common	openat2			sys_openat2			sys_openat2
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 016a727d4357..d5ba779f92c2 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -436,3 +436,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index e047480b1605..d45bebdfdfae 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -479,3 +479,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2			sys_openat2
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index ad968b7bac72..88825c5e631f 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -438,3 +438,4 @@
 431	i386	fsconfig		sys_fsconfig			__ia32_sys_fsconfig
 432	i386	fsmount			sys_fsmount			__ia32_sys_fsmount
 433	i386	fspick			sys_fspick			__ia32_sys_fspick
+437	i386	openat2			sys_openat2			__ia32_sys_openat2
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index b4e6f9e6204a..ebfde1799001 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -355,6 +355,7 @@
 431	common	fsconfig		__x64_sys_fsconfig
 432	common	fsmount			__x64_sys_fsmount
 433	common	fspick			__x64_sys_fspick
+437	common	openat2			__x64_sys_openat2
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index 5fa0ee1c8e00..927a642859a1 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -404,3 +404,4 @@
 431	common	fsconfig			sys_fsconfig
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
+437	common	openat2				sys_openat2
diff --git a/fs/open.c b/fs/open.c
index bdca45528524..062761136f21 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -928,19 +928,29 @@ struct file *open_with_fake_path(const struct path *path, int flags,
 }
 EXPORT_SYMBOL(open_with_fake_path);
 
-static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
+static inline int build_open_flags(const struct open_how *how,
+				   struct open_flags *op)
 {
+	int flags = how->flags;
 	int lookup_flags = 0;
+	int opath_mask = 0;
 	int acc_mode = ACC_MODE(flags);
 
 	/*
-	 * Clear out all open flags we don't know about so that we don't report
-	 * them in fcntl(F_GETFD) or similar interfaces.
+	 * Older syscalls still clear these bits before calling
+	 * build_open_flags(), but openat2(2) checks all its arguments.
 	 */
-	flags &= VALID_OPEN_FLAGS;
+	if (flags & ~VALID_OPEN_FLAGS)
+		return -EINVAL;
+	if (how->resolve & ~VALID_RESOLVE_FLAGS)
+		return -EINVAL;
+	if (!(how->flags & (O_PATH | O_CREAT | __O_TMPFILE)) && how->mode != 0)
+		return -EINVAL;
+	if (memchr_inv(how->reserved, 0, sizeof(how->reserved)))
+		return -EINVAL;
 
 	if (flags & (O_CREAT | __O_TMPFILE))
-		op->mode = (mode & S_IALLUGO) | S_IFREG;
+		op->mode = (how->mode & S_IALLUGO) | S_IFREG;
 	else
 		op->mode = 0;
 
@@ -968,6 +978,14 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 		 */
 		flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
 		acc_mode = 0;
+
+		/* Allow userspace to restrict the re-opening of O_PATH fds. */
+		if (how->upgrade_mask & ~VALID_UPGRADE_FLAGS)
+			return -EINVAL;
+		if (!(how->upgrade_mask & UPGRADE_NOREAD))
+			opath_mask |= FMODE_PATH_READ;
+		if (!(how->upgrade_mask & UPGRADE_NOWRITE))
+			opath_mask |= FMODE_PATH_WRITE;
 	}
 
 	op->open_flag = flags;
@@ -983,8 +1001,7 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 
 	op->acc_mode = acc_mode;
 	op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
-	/* For O_PATH backwards-compatibility we default to an all-set mask. */
-	op->opath_mask = FMODE_PATH_READ | FMODE_PATH_WRITE;
+	op->opath_mask = opath_mask;
 
 	if (flags & O_CREAT) {
 		op->intent |= LOOKUP_CREATE;
@@ -998,6 +1015,18 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 		lookup_flags |= LOOKUP_FOLLOW;
 	if (flags & O_EMPTYPATH)
 		lookup_flags |= LOOKUP_EMPTY;
+
+	if (how->resolve & RESOLVE_NO_XDEV)
+		lookup_flags |= LOOKUP_NO_XDEV;
+	if (how->resolve & RESOLVE_NO_MAGICLINKS)
+		lookup_flags |= LOOKUP_NO_MAGICLINKS;
+	if (how->resolve & RESOLVE_NO_SYMLINKS)
+		lookup_flags |= LOOKUP_NO_SYMLINKS;
+	if (how->resolve & RESOLVE_BENEATH)
+		lookup_flags |= LOOKUP_BENEATH;
+	if (how->resolve & RESOLVE_IN_ROOT)
+		lookup_flags |= LOOKUP_IN_ROOT;
+
 	op->lookup_flags = lookup_flags;
 	return 0;
 }
@@ -1016,8 +1045,14 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 struct file *file_open_name(struct filename *name, int flags, umode_t mode)
 {
 	struct open_flags op;
-	int err = build_open_flags(flags, mode, &op);
-	return err ? ERR_PTR(err) : do_filp_open(AT_FDCWD, name, &op);
+	struct open_how how = {
+		.flags = flags & VALID_OPEN_FLAGS,
+		.mode = OPENHOW_MODE(flags, mode),
+	};
+	int err = build_open_flags(&how, &op);
+	if (err)
+		return ERR_PTR(err);
+	return do_filp_open(AT_FDCWD, name, &op);
 }
 
 /**
@@ -1048,17 +1083,22 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
 			    const char *filename, int flags, umode_t mode)
 {
 	struct open_flags op;
-	int err = build_open_flags(flags, mode, &op);
+	struct open_how how = {
+		.flags = flags & VALID_OPEN_FLAGS,
+		.mode = OPENHOW_MODE(flags, mode),
+	};
+	int err = build_open_flags(&how, &op);
 	if (err)
 		return ERR_PTR(err);
 	return do_file_open_root(dentry, mnt, filename, &op);
 }
 EXPORT_SYMBOL(file_open_root);
 
-long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
+long do_sys_open(int dfd, const char __user *filename,
+		 struct open_how *how)
 {
 	struct open_flags op;
-	int fd = build_open_flags(flags, mode, &op);
+	int fd = build_open_flags(how, &op);
 	int empty = 0;
 	struct filename *tmp;
 
@@ -1071,7 +1111,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 	if (!empty)
 		op.open_flag &= ~O_EMPTYPATH;
 
-	fd = get_unused_fd_flags(flags);
+	fd = get_unused_fd_flags(how->flags);
 	if (fd >= 0) {
 		struct file *f = do_filp_open(dfd, tmp, &op);
 		if (IS_ERR(f)) {
@@ -1088,19 +1128,35 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 
 SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
 {
-	if (force_o_largefile())
-		flags |= O_LARGEFILE;
-
-	return do_sys_open(AT_FDCWD, filename, flags, mode);
+	return ksys_open(filename, flags, mode);
 }
 
 SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
 		umode_t, mode)
 {
+	struct open_how how = {
+		.flags = flags & VALID_OPEN_FLAGS,
+		.mode = OPENHOW_MODE(flags, mode),
+	};
+
+	if (force_o_largefile())
+		how.flags |= O_LARGEFILE;
+
+	return do_sys_open(dfd, filename, &how);
+}
+
+SYSCALL_DEFINE3(openat2, int, dfd, const char __user *, filename,
+		const struct open_how __user *, how)
+{
+	struct open_how tmp;
+
+	if (copy_from_user(&tmp, how, sizeof(tmp)))
+		return -EFAULT;
+
 	if (force_o_largefile())
-		flags |= O_LARGEFILE;
+		tmp.flags |= O_LARGEFILE;
 
-	return do_sys_open(dfd, filename, flags, mode);
+	return do_sys_open(dfd, filename, &tmp);
 }
 
 #ifdef CONFIG_COMPAT
@@ -1110,7 +1166,11 @@ SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
  */
 COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
 {
-	return do_sys_open(AT_FDCWD, filename, flags, mode);
+	struct open_how how = {
+		.flags = flags & VALID_OPEN_FLAGS,
+		.mode = OPENHOW_MODE(flags, mode),
+	};
+	return do_sys_open(AT_FDCWD, filename, &how);
 }
 
 /*
@@ -1119,7 +1179,11 @@ COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t,
  */
 COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode)
 {
-	return do_sys_open(dfd, filename, flags, mode);
+	struct open_how how = {
+		.flags = flags & VALID_OPEN_FLAGS,
+		.mode = OPENHOW_MODE(flags, mode),
+	};
+	return do_sys_open(dfd, filename, &how);
 }
 #endif
 
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index 2868ae6c8fc1..f7f378e1f43c 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -4,13 +4,26 @@
 
 #include <uapi/linux/fcntl.h>
 
-/* list of all valid flags for the open/openat flags argument: */
+/* Should open_how.mode be set for older syscalls wrappers? */
+#define OPENHOW_MODE(flags, mode) \
+	(((flags) & (O_CREAT | __O_TMPFILE)) ? (mode) : 0)
+
+/* List of all valid flags for the open/openat flags argument: */
 #define VALID_OPEN_FLAGS \
 	(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
 	 O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | __O_SYNC | O_DSYNC | \
 	 FASYNC	| O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
 	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTYPATH)
 
+/* List of all valid flags for the how->upgrade_mask argument: */
+#define VALID_UPGRADE_FLAGS \
+	(UPGRADE_NOWRITE | UPGRADE_NOREAD)
+
+/* List of all valid flags for the how->resolve argument: */
+#define VALID_RESOLVE_FLAGS \
+	(RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \
+	 RESOLVE_BENEATH | RESOLVE_IN_ROOT)
+
 #ifndef force_o_largefile
 #define force_o_largefile() (!IS_ENABLED(CONFIG_ARCH_32BIT_OFF_T))
 #endif
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7df213405ea..a3aede2b3a91 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2515,8 +2515,8 @@ extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
 		       struct file *filp);
 extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
 			loff_t len);
-extern long do_sys_open(int dfd, const char __user *filename, int flags,
-			umode_t mode);
+extern long do_sys_open(int dfd, const char __user *filename,
+			struct open_how *how);
 extern struct file *file_open_name(struct filename *, int, umode_t);
 extern struct file *filp_open(const char *, int, umode_t);
 extern struct file *file_open_root(struct dentry *, struct vfsmount *,
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 2bcef4c70183..db141c67c977 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -68,6 +68,7 @@ struct sigaltstack;
 struct rseq;
 union bpf_attr;
 struct io_uring_params;
+struct open_how;
 
 #include <linux/types.h>
 #include <linux/aio_abi.h>
@@ -438,6 +439,8 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
 asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group);
 asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
 			   umode_t mode);
+asmlinkage long sys_openat2(int dfd, const char __user *filename,
+			    const struct open_how *how);
 asmlinkage long sys_close(unsigned int fd);
 asmlinkage long sys_vhangup(void);
 
@@ -1369,15 +1372,21 @@ static inline int ksys_close(unsigned int fd)
 	return __close_fd(current->files, fd);
 }
 
-extern long do_sys_open(int dfd, const char __user *filename, int flags,
-			umode_t mode);
+extern long do_sys_open(int dfd, const char __user *filename,
+			struct open_how *how);
 
 static inline long ksys_open(const char __user *filename, int flags,
 			     umode_t mode)
 {
+	struct open_how how = {
+		.flags = flags & VALID_OPEN_FLAGS,
+		.mode = OPENHOW_MODE(flags, mode),
+	};
+
 	if (force_o_largefile())
-		flags |= O_LARGEFILE;
-	return do_sys_open(AT_FDCWD, filename, flags, mode);
+		how.flags |= O_LARGEFILE;
+
+	return do_sys_open(AT_FDCWD, filename, &how);
 }
 
 extern long do_sys_truncate(const char __user *pathname, loff_t length);
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index a87904daf103..e4e8eb7b20c1 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -844,9 +844,11 @@ __SYSCALL(__NR_fsconfig, sys_fsconfig)
 __SYSCALL(__NR_fsmount, sys_fsmount)
 #define __NR_fspick 433
 __SYSCALL(__NR_fspick, sys_fspick)
+#define __NR_openat2 437
+__SYSCALL(__NR_openat2, sys_openat2)
 
 #undef __NR_syscalls
-#define __NR_syscalls 434
+#define __NR_syscalls 438
 
 /*
  * 32 bit systems traditionally used different
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
index 1d338357df8a..ebfc97b3d8aa 100644
--- a/include/uapi/linux/fcntl.h
+++ b/include/uapi/linux/fcntl.h
@@ -93,5 +93,47 @@
 
 #define AT_RECURSIVE		0x8000	/* Apply to the entire subtree */
 
+/**
+ * Arguments for how openat2(2) should open the target path. If @resolve is
+ * zero, then openat2(2) operates identically to openat(2).
+ *
+ * However, unlike openat(2), unknown bits in @flags result in -EINVAL rather
+ * than being silently ignored. In addition, @mode (or @upgrade_mask) must be
+ * zero unless one of {O_CREAT, O_TMPFILE, O_PATH} are set.
+ *
+ * @flags: O_* flags.
+ * @mode: O_CREAT/O_TMPFILE file mode.
+ * @upgrade_mask: UPGRADE_* flags (to restrict O_PATH re-opening).
+ * @resolve: RESOLVE_* flags.
+ * @reserved: reserved for future extensions, must be zeroed.
+ */
+struct open_how {
+	__u32 flags;
+	union {
+		__u16 mode;
+		__u16 upgrade_mask;
+	};
+	__u16 resolve;
+	__u64 reserved[7]; /* must be zeroed */
+};
+
+/* how->resolve flags for openat2(2). */
+#define RESOLVE_NO_XDEV		0x01 /* Block mount-point crossings
+					(includes bind-mounts). */
+#define RESOLVE_NO_MAGICLINKS	0x02 /* Block traversal through procfs-style
+					"magic-links". */
+#define RESOLVE_NO_SYMLINKS	0x04 /* Block traversal through all symlinks
+					(implies OEXT_NO_MAGICLINKS) */
+#define RESOLVE_BENEATH		0x08 /* Block "lexical" trickery like
+					"..", symlinks, and absolute
+					paths which escape the dirfd. */
+#define RESOLVE_IN_ROOT		0x10 /* Make all jumps to "/" and ".."
+					be scoped inside the dirfd
+					(similar to chroot(2)). */
+
+/* how->upgrade flags for openat2(2). */
+/* First bit is reserved for a future UPGRADE_NOEXEC flag. */
+#define UPGRADE_NOREAD		0x02 /* Block re-opening with MAY_READ. */
+#define UPGRADE_NOWRITE		0x04 /* Block re-opening with MAY_WRITE. */
 
 #endif /* _UAPI_LINUX_FCNTL_H */
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 8/9] kselftest: save-and-restore errno to allow for %m formatting
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

Previously, using "%m" in a ksft_* format string can result in strange
output because the errno value wasn't saved before calling other libc
functions. The solution is to simply save and restore the errno before
we format the user-supplied format string.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 tools/testing/selftests/kselftest.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h
index ec15c4f6af55..0ac49d91a260 100644
--- a/tools/testing/selftests/kselftest.h
+++ b/tools/testing/selftests/kselftest.h
@@ -10,6 +10,7 @@
 #ifndef __KSELFTEST_H
 #define __KSELFTEST_H
 
+#include <errno.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdarg.h>
@@ -81,58 +82,68 @@ static inline void ksft_print_cnts(void)
 
 static inline void ksft_print_msg(const char *msg, ...)
 {
+	int saved_errno = errno;
 	va_list args;
 
 	va_start(args, msg);
 	printf("# ");
+	errno = saved_errno;
 	vprintf(msg, args);
 	va_end(args);
 }
 
 static inline void ksft_test_result_pass(const char *msg, ...)
 {
+	int saved_errno = errno;
 	va_list args;
 
 	ksft_cnt.ksft_pass++;
 
 	va_start(args, msg);
 	printf("ok %d ", ksft_test_num());
+	errno = saved_errno;
 	vprintf(msg, args);
 	va_end(args);
 }
 
 static inline void ksft_test_result_fail(const char *msg, ...)
 {
+	int saved_errno = errno;
 	va_list args;
 
 	ksft_cnt.ksft_fail++;
 
 	va_start(args, msg);
 	printf("not ok %d ", ksft_test_num());
+	errno = saved_errno;
 	vprintf(msg, args);
 	va_end(args);
 }
 
 static inline void ksft_test_result_skip(const char *msg, ...)
 {
+	int saved_errno = errno;
 	va_list args;
 
 	ksft_cnt.ksft_xskip++;
 
 	va_start(args, msg);
 	printf("not ok %d # SKIP ", ksft_test_num());
+	errno = saved_errno;
 	vprintf(msg, args);
 	va_end(args);
 }
 
 static inline void ksft_test_result_error(const char *msg, ...)
 {
+	int saved_errno = errno;
 	va_list args;
 
 	ksft_cnt.ksft_error++;
 
 	va_start(args, msg);
 	printf("not ok %d # error ", ksft_test_num());
+	errno = saved_errno;
 	vprintf(msg, args);
 	va_end(args);
 }
@@ -152,10 +163,12 @@ static inline int ksft_exit_fail(void)
 
 static inline int ksft_exit_fail_msg(const char *msg, ...)
 {
+	int saved_errno = errno;
 	va_list args;
 
 	va_start(args, msg);
 	printf("Bail out! ");
+	errno = saved_errno;
 	vprintf(msg, args);
 	va_end(args);
 
@@ -178,10 +191,12 @@ static inline int ksft_exit_xpass(void)
 static inline int ksft_exit_skip(const char *msg, ...)
 {
 	if (msg) {
+		int saved_errno = errno;
 		va_list args;
 
 		va_start(args, msg);
 		printf("not ok %d # SKIP ", 1 + ksft_test_num());
+		errno = saved_errno;
 		vprintf(msg, args);
 		va_end(args);
 	} else {
-- 
2.22.0


^ permalink raw reply related

* [PATCH v10 9/9] selftests: add openat2(2) selftests
From: Aleksa Sarai @ 2019-07-19 16:42 UTC (permalink / raw)
  To: Al Viro, Jeff Layton, J. Bruce Fields, Arnd Bergmann,
	David Howells, Shuah Khan, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, Aleksa Sarai,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-m68k, linux-api, Chanho Min, linux-kernel, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <20190719164225.27083-1-cyphar@cyphar.com>

Test all of the various openat2(2) flags, as well as how file
descriptor re-opening works. A small stress-test of a symlink-rename
attack is included to show that the protections against ".."-based
attacks are sufficient.

In addition, the memfd selftest is fixed to no longer depend on the
now-disallowed functionality of upgrading an O_RDONLY descriptor to
O_RDWR.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/memfd/memfd_test.c    |   7 +-
 tools/testing/selftests/openat2/.gitignore    |   1 +
 tools/testing/selftests/openat2/Makefile      |   8 +
 tools/testing/selftests/openat2/helpers.c     | 162 +++++++
 tools/testing/selftests/openat2/helpers.h     | 114 +++++
 .../testing/selftests/openat2/linkmode_test.c | 326 ++++++++++++++
 .../selftests/openat2/rename_attack_test.c    | 124 ++++++
 .../testing/selftests/openat2/resolve_test.c  | 397 ++++++++++++++++++
 9 files changed, 1138 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/openat2/.gitignore
 create mode 100644 tools/testing/selftests/openat2/Makefile
 create mode 100644 tools/testing/selftests/openat2/helpers.c
 create mode 100644 tools/testing/selftests/openat2/helpers.h
 create mode 100644 tools/testing/selftests/openat2/linkmode_test.c
 create mode 100644 tools/testing/selftests/openat2/rename_attack_test.c
 create mode 100644 tools/testing/selftests/openat2/resolve_test.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 9781ca79794a..42a27d029c10 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -37,6 +37,7 @@ TARGETS += powerpc
 TARGETS += proc
 TARGETS += pstore
 TARGETS += ptrace
+TARGETS += openat2
 TARGETS += rseq
 TARGETS += rtc
 TARGETS += seccomp
diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c
index c67d32eeb668..e71df3d3e55d 100644
--- a/tools/testing/selftests/memfd/memfd_test.c
+++ b/tools/testing/selftests/memfd/memfd_test.c
@@ -925,7 +925,7 @@ static void test_share_mmap(char *banner, char *b_suffix)
  */
 static void test_share_open(char *banner, char *b_suffix)
 {
-	int fd, fd2;
+	int procfd, fd, fd2;
 
 	printf("%s %s %s\n", memfd_str, banner, b_suffix);
 
@@ -950,13 +950,16 @@ static void test_share_open(char *banner, char *b_suffix)
 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);
 	mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK);
 
+	/* We cannot do a MAY_WRITE re-open of an O_RDONLY fd. */
+	procfd = mfd_assert_open(fd2, O_PATH, 0);
 	close(fd2);
-	fd2 = mfd_assert_open(fd, O_RDWR, 0);
+	fd2 = mfd_assert_open(procfd, O_WRONLY, 0);
 
 	mfd_assert_add_seals(fd2, F_SEAL_SEAL);
 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);
 	mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);
 
+	close(procfd);
 	close(fd2);
 	close(fd);
 }
diff --git a/tools/testing/selftests/openat2/.gitignore b/tools/testing/selftests/openat2/.gitignore
new file mode 100644
index 000000000000..bd68f6c3fd07
--- /dev/null
+++ b/tools/testing/selftests/openat2/.gitignore
@@ -0,0 +1 @@
+/*_test
diff --git a/tools/testing/selftests/openat2/Makefile b/tools/testing/selftests/openat2/Makefile
new file mode 100644
index 000000000000..a0c1b53fd268
--- /dev/null
+++ b/tools/testing/selftests/openat2/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CFLAGS += -Wall -O2 -g
+TEST_GEN_PROGS := linkmode_test resolve_test rename_attack_test
+
+include ../lib.mk
+
+$(TEST_GEN_PROGS): helpers.c
diff --git a/tools/testing/selftests/openat2/helpers.c b/tools/testing/selftests/openat2/helpers.c
new file mode 100644
index 000000000000..c16213ff1946
--- /dev/null
+++ b/tools/testing/selftests/openat2/helpers.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author: Aleksa Sarai <cyphar@cyphar.com>
+ * Copyright (C) 2018-2019 SUSE LLC.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <string.h>
+#include <syscall.h>
+#include <limits.h>
+
+#include "helpers.h"
+
+int sys_openat2(int dfd, const char *path, const struct open_how *how)
+{
+	int ret = syscall(__NR_openat2, dfd, path, how);
+	return ret >= 0 ? ret : -errno;
+}
+
+int sys_openat(int dfd, const char *path, const struct open_how *how)
+{
+	int ret = openat(dfd, path, how->flags, how->mode);
+	return ret >= 0 ? ret : -errno;
+}
+
+int sys_renameat2(int olddirfd, const char *oldpath,
+		  int newdirfd, const char *newpath, unsigned int flags)
+{
+	int ret = syscall(__NR_renameat2, olddirfd, oldpath,
+					  newdirfd, newpath, flags);
+	return ret >= 0 ? ret : -errno;
+}
+
+char *openat_flags(unsigned int flags)
+{
+	char *flagset, *accmode = "(none)";
+
+	switch (flags & 0x03) {
+		case O_RDWR:
+			accmode = "O_RDWR";
+			break;
+		case O_RDONLY:
+			accmode = "O_RDONLY";
+			break;
+		case O_WRONLY:
+			accmode = "O_WRONLY";
+			break;
+	}
+
+	E_asprintf(&flagset, "%s%s%s",
+		   (flags & O_PATH) ? "O_PATH|" : "",
+		   (flags & O_CREAT) ? "O_CREAT|" : "",
+		   accmode);
+
+	return flagset;
+}
+
+char *openat2_flags(const struct open_how *how)
+{
+	char *p;
+	char *flags_set, *resolve_set, *acc_set, *set;
+
+	flags_set = openat_flags(how->flags);
+
+	E_asprintf(&resolve_set, "%s%s%s%s%s0",
+		   (how->resolve & RESOLVE_NO_XDEV) ? "RESOLVE_NO_XDEV|" : "",
+		   (how->resolve & RESOLVE_NO_MAGICLINKS) ? "RESOLVE_NO_MAGICLINKS|" : "",
+		   (how->resolve & RESOLVE_NO_SYMLINKS) ? "RESOLVE_NO_SYMLINKS|" : "",
+		   (how->resolve & RESOLVE_BENEATH) ? "RESOLVE_BENEATH|" : "",
+		   (how->resolve & RESOLVE_IN_ROOT) ? "RESOLVE_IN_ROOT|" : "");
+
+	/* Remove trailing "|0". */
+	p = strstr(resolve_set, "|0");
+	if (p)
+		*p = '\0';
+
+	if (how->flags & O_PATH)
+		E_asprintf(&acc_set, ", upgrade_mask=%s%s0",
+			   (how->upgrade_mask & UPGRADE_NOREAD) ? "UPGRADE_NOREAD|" : "",
+			   (how->upgrade_mask & UPGRADE_NOWRITE) ? "UPGRADE_NOWRITE|" : "");
+	else if (how->flags & O_CREAT)
+		E_asprintf(&acc_set, ", mode=0%o", how->mode);
+	else
+		acc_set = strdup("");
+
+	/* Remove trailing "|0". */
+	p = strstr(acc_set, "|0");
+	if (p)
+		*p = '\0';
+
+	/* And now generate our flagset. */
+	E_asprintf(&set, "[flags=%s, resolve=%s%s]",
+		   flags_set, resolve_set, acc_set);
+
+	free(flags_set);
+	free(resolve_set);
+	free(acc_set);
+	return set;
+}
+
+int touchat(int dfd, const char *path)
+{
+	int fd = openat(dfd, path, O_CREAT);
+	if (fd >= 0)
+		close(fd);
+	return fd;
+}
+
+char *fdreadlink(int fd)
+{
+	char *target, *tmp;
+
+	E_asprintf(&tmp, "/proc/self/fd/%d", fd);
+
+	target = malloc(PATH_MAX);
+	if (!target)
+		ksft_exit_fail_msg("fdreadlink: malloc failed\n");
+	memset(target, 0, PATH_MAX);
+
+	E_readlink(tmp, target, PATH_MAX);
+	free(tmp);
+	return target;
+}
+
+bool fdequal(int fd, int dfd, const char *path)
+{
+	char *fdpath, *dfdpath, *other;
+	bool cmp;
+
+	fdpath = fdreadlink(fd);
+	dfdpath = fdreadlink(dfd);
+
+	if (!path)
+		E_asprintf(&other, "%s", dfdpath);
+	else if (*path == '/')
+		E_asprintf(&other, "%s", path);
+	else
+		E_asprintf(&other, "%s/%s", dfdpath, path);
+
+	cmp = !strcmp(fdpath, other);
+	if (!cmp)
+		ksft_print_msg("fdequal: expected '%s' but got '%s'\n", other, fdpath);
+
+	free(fdpath);
+	free(dfdpath);
+	free(other);
+	return cmp;
+}
+
+void test_openat2_supported(void)
+{
+	struct open_how how = {};
+	int fd = sys_openat2(AT_FDCWD, ".", &how);
+	if (fd == -ENOSYS)
+		ksft_exit_skip("openat2(2) unsupported on this kernel\n");
+	if (fd < 0)
+		ksft_exit_fail_msg("openat2(2) supported check failed: %s\n", strerror(-fd));
+	close(fd);
+}
diff --git a/tools/testing/selftests/openat2/helpers.h b/tools/testing/selftests/openat2/helpers.h
new file mode 100644
index 000000000000..93d1154c1aea
--- /dev/null
+++ b/tools/testing/selftests/openat2/helpers.h
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author: Aleksa Sarai <cyphar@cyphar.com>
+ * Copyright (C) 2018-2019 SUSE LLC.
+ */
+
+#ifndef __RESOLVEAT_H__
+#define __RESOLVEAT_H__
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include "../kselftest.h"
+
+#define ARRAY_LEN(X) (sizeof (X) / sizeof (*(X)))
+
+#ifndef SYS_openat2
+#ifndef __NR_openat2
+#define __NR_openat2 437
+#endif /* __NR_openat2 */
+#define SYS_openat2 __NR_openat2
+#endif /* SYS_openat2 */
+
+/**
+ * Arguments for how openat2(2) should open the target path. If @extra is zero,
+ * then openat2 is identical to openat(2). Only one of @mode or @upgrade_mask
+ * may be set at any given time.
+ *
+ * @flags: O_* flags (unknown flags ignored).
+ * @mode: O_CREAT file mode (ignored otherwise).
+ * @upgrade_mask: restrict how the O_PATH may be re-opened (ignored otherwise).
+ * @resolve: RESOLVE_* flags (-EINVAL on unknown flags).
+ * @reserved: reserved for future extensions, must be zeroed.
+ */
+struct open_how {
+	uint32_t flags;
+	union {
+		uint16_t mode;
+		uint16_t upgrade_mask;
+	};
+	uint16_t resolve;
+	uint64_t reserved[7]; /* must be zeroed */
+};
+
+#ifndef RESOLVE_INROOT
+/* how->resolve flags for openat2(2). */
+#define RESOLVE_NO_XDEV		0x01 /* Block mount-point crossings
+					(includes bind-mounts). */
+#define RESOLVE_NO_MAGICLINKS	0x02 /* Block traversal through procfs-style
+					"magic-links". */
+#define RESOLVE_NO_SYMLINKS	0x04 /* Block traversal through all symlinks
+					(implies OEXT_NO_MAGICLINKS) */
+#define RESOLVE_BENEATH		0x08 /* Block "lexical" trickery like
+					"..", symlinks, and absolute
+					paths which escape the dirfd. */
+#define RESOLVE_IN_ROOT		0x10 /* Make all jumps to "/" and ".."
+					be scoped inside the dirfd
+					(similar to chroot(2)). */
+#endif /* RESOLVE_IN_ROOT */
+
+#ifndef UPGRADE_NOREAD
+/* how->upgrade flags for openat2(2). */
+/* First bit is reserved for a future UPGRADE_NOEXEC flag. */
+#define UPGRADE_NOREAD		0x02 /* Block re-opening with MAY_READ. */
+#define UPGRADE_NOWRITE		0x04 /* Block re-opening with MAY_WRITE. */
+#endif /* UPGRADE_NOREAD */
+
+#ifndef O_EMPTYPATH
+#define	O_EMPTYPATH 040000000
+#endif /* O_EMPTYPATH */
+
+#define E_func(func, ...)						\
+	do {								\
+		if (func(__VA_ARGS__) < 0)				\
+			ksft_exit_fail_msg("%s:%d %s failed\n", \
+					   __FILE__, __LINE__, #func);\
+	} while (0)
+
+#define E_mkdirat(...)   E_func(mkdirat,   __VA_ARGS__)
+#define E_symlinkat(...) E_func(symlinkat, __VA_ARGS__)
+#define E_touchat(...)   E_func(touchat,   __VA_ARGS__)
+#define E_readlink(...)  E_func(readlink,  __VA_ARGS__)
+#define E_fstatat(...)   E_func(fstatat,   __VA_ARGS__)
+#define E_asprintf(...)  E_func(asprintf,  __VA_ARGS__)
+#define E_fchdir(...)    E_func(fchdir,    __VA_ARGS__)
+#define E_mount(...)     E_func(mount,     __VA_ARGS__)
+#define E_unshare(...)   E_func(unshare,   __VA_ARGS__)
+#define E_setresuid(...) E_func(setresuid, __VA_ARGS__)
+#define E_chmod(...)     E_func(chmod,     __VA_ARGS__)
+
+#define E_assert(expr, msg, ...)					\
+	do {								\
+		if (!(expr))						\
+			ksft_exit_fail_msg("ASSERT(%s:%d) failed (%s): " msg "\n", \
+					   __FILE__, __LINE__, #expr, ##__VA_ARGS__); \
+	} while (0)
+
+typedef int (*openfunc_t)(int dfd, const char *path, const struct open_how *how);
+
+int sys_openat2(int dfd, const char *path, const struct open_how *how);
+char *openat2_flags(const struct open_how *how);
+
+int sys_openat(int dfd, const char *path, const struct open_how *how);
+char *openat_flags(unsigned int flags);
+
+int sys_renameat2(int olddirfd, const char *oldpath,
+		  int newdirfd, const char *newpath, unsigned int flags);
+
+int touchat(int dfd, const char *path);
+char *fdreadlink(int fd);
+bool fdequal(int fd, int dfd, const char *path);
+
+void test_openat2_supported(void);
+
+#endif /* __RESOLVEAT_H__ */
diff --git a/tools/testing/selftests/openat2/linkmode_test.c b/tools/testing/selftests/openat2/linkmode_test.c
new file mode 100644
index 000000000000..87d94413659f
--- /dev/null
+++ b/tools/testing/selftests/openat2/linkmode_test.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author: Aleksa Sarai <cyphar@cyphar.com>
+ * Copyright (C) 2018-2019 SUSE LLC.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "../kselftest.h"
+#include "helpers.h"
+
+static mode_t fdmode(int fd)
+{
+	char *fdpath;
+	struct stat statbuf;
+	mode_t mode;
+
+	E_asprintf(&fdpath, "/proc/self/fd/%d", fd);
+	E_fstatat(AT_FDCWD, fdpath, &statbuf, AT_SYMLINK_NOFOLLOW);
+	mode = (statbuf.st_mode & ~S_IFMT);
+	free(fdpath);
+
+	return mode;
+}
+
+static int reopen_proc(int fd, unsigned int flags)
+{
+	int ret, saved_errno;
+	char *fdpath;
+
+	E_asprintf(&fdpath, "/proc/self/fd/%d", fd);
+	ret = open(fdpath, flags);
+	saved_errno = errno;
+	free(fdpath);
+
+	return ret >= 0 ? ret : -saved_errno;
+}
+
+static int reopen_oemptypath(int fd, unsigned int flags)
+{
+	int ret = openat(fd, "", O_EMPTYPATH | flags);
+	return ret >= 0 ? ret : -errno;
+}
+
+struct reopen_test {
+	openfunc_t open;
+	mode_t chmod_mode;
+	struct {
+		struct open_how how;
+		mode_t mode;
+		int err;
+	} orig, new;
+};
+
+static bool reopen(int fd, struct reopen_test *test)
+{
+	int newfd;
+	mode_t proc_mode;
+	bool failed = false;
+
+	/* Check that the proc mode is correct. */
+	proc_mode = fdmode(fd);
+	if (proc_mode != test->orig.mode) {
+		ksft_print_msg("incorrect fdmode (got[%o] != want[%o])\n",
+			       proc_mode, test->orig.mode);
+		failed = true;
+	}
+
+	/* Re-open through /proc. */
+	newfd = reopen_proc(fd, test->new.how.flags);
+	if (newfd != test->new.err && (newfd < 0 || test->new.err < 0)) {
+		ksft_print_msg("/proc failure (%d != %d [%s])\n",
+			       newfd, test->new.err, strerror(-test->new.err));
+		failed = true;
+	}
+	if (newfd >= 0) {
+		proc_mode = fdmode(newfd);
+		if (proc_mode != test->new.mode) {
+			ksft_print_msg("/proc wrong fdmode (got[%o] != want[%o])\n",
+				       proc_mode, test->new.mode);
+			failed = true;
+		}
+		close(newfd);
+	}
+
+	/* Re-open with O_EMPTYPATH. */
+	newfd = reopen_oemptypath(fd, test->new.how.flags);
+	if (newfd != test->new.err && (newfd < 0 || test->new.err < 0)) {
+		ksft_print_msg("O_EMPTYPATH failure (%d != %d [%s])\n",
+			       newfd, test->new.err, strerror(-test->new.err));
+		failed = true;
+	}
+	if (newfd >= 0) {
+		proc_mode = fdmode(newfd);
+		if (proc_mode != test->new.mode) {
+			ksft_print_msg("O_EMPTYPATH wrong fdmode (got[%o] != want[%o])\n",
+				       proc_mode, test->new.mode);
+			failed = true;
+		}
+		close(newfd);
+	}
+
+	return failed;
+}
+
+void test_reopen_ordinary(bool privileged)
+{
+	int fd;
+	int err_access = privileged ? 0 : -EACCES;
+	char tmpfile[] = "/tmp/ksft-openat2-reopen-testfile.XXXXXX";
+
+	fd = mkstemp(tmpfile);
+	E_assert(fd >= 0, "mkstemp failed: %m\n");
+	close(fd);
+
+	struct reopen_test tests[] = {
+		/* Re-opening with the same mode should succeed. */
+		{ .open = sys_openat,	  .chmod_mode = 0400,
+		  .orig.how.flags = O_RDONLY, .orig.mode  = 0500,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500 },
+		{ .open = sys_openat,	  .chmod_mode = 0200,
+		  .orig.how.flags = O_WRONLY, .orig.mode  = 0300,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300 },
+		{ .open = sys_openat,	  .chmod_mode = 0600,
+		  .orig.how.flags =   O_RDWR, .orig.mode  = 0700,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700 },
+		{ .open = sys_openat,	  .chmod_mode = 0600,
+		  .orig.how.flags =   O_RDWR, .orig.mode  = 0700,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500 },
+		{ .open = sys_openat,	  .chmod_mode = 0600,
+		  .orig.how.flags =   O_RDWR, .orig.mode  = 0700,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300 },
+
+		/*
+		 * Re-opening with a different mode will always fail (with an obvious
+		 * carve-out for privileged users).
+		 */
+		{ .open = sys_openat,	  .chmod_mode = 0600,
+		  .orig.how.flags = O_RDONLY, .orig.mode  = 0500,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300, .new.err = err_access },
+		{ .open = sys_openat,	  .chmod_mode = 0600,
+		  .orig.how.flags = O_WRONLY, .orig.mode  = 0300,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500, .new.err = err_access },
+		{ .open = sys_openat,	  .chmod_mode = 0600,
+		  .orig.how.flags = O_RDONLY, .orig.mode  = 0500,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700, .new.err = err_access },
+		{ .open = sys_openat,	  .chmod_mode = 0600,
+		  .orig.how.flags = O_WRONLY, .orig.mode  = 0300,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700, .new.err = err_access },
+
+		/* Doubly so if they didn't even have permissions at open-time. */
+		{ .open = sys_openat,	  .chmod_mode = 0400,
+		  .orig.how.flags = O_RDONLY, .orig.mode  = 0500,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300, .new.err = err_access },
+		{ .open = sys_openat,	  .chmod_mode = 0200,
+		  .orig.how.flags = O_WRONLY, .orig.mode  = 0300,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500, .new.err = err_access },
+		{ .open = sys_openat,	  .chmod_mode = 0400,
+		  .orig.how.flags = O_RDONLY, .orig.mode  = 0500,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700, .new.err = err_access },
+		{ .open = sys_openat,	  .chmod_mode = 0200,
+		  .orig.how.flags = O_WRONLY, .orig.mode  = 0300,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700, .new.err = err_access },
+
+		/* O_PATH re-opens (of ordinary files) will always work. */
+		{ .open = sys_openat,	  .chmod_mode = 0000,
+		  .orig.how.flags =   O_PATH, .orig.mode  = 0070,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300 },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags =   O_PATH, .orig.mode  = 0070,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300 },
+
+		{ .open = sys_openat,	  .chmod_mode = 0000,
+		  .orig.how.flags =   O_PATH, .orig.mode  = 0070,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500 },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags =   O_PATH, .orig.mode  = 0070,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500 },
+
+		{ .open = sys_openat,	  .chmod_mode = 0000,
+		  .orig.how.flags =   O_PATH, .orig.mode  = 0070,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700 },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags =   O_PATH, .orig.mode  = 0070,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700 },
+
+		/*
+		 * openat2(2) UPGRADE_NO* flags. In the privileged case, the re-open
+		 * will work but the mode will still be scoped to the mode (or'd with
+		 * the open acc_mode).
+		 */
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0010,
+		  .orig.how.upgrade_mask = UPGRADE_NOREAD | UPGRADE_NOWRITE,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500, .new.err = err_access },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0010,
+		  .orig.how.upgrade_mask = UPGRADE_NOREAD | UPGRADE_NOWRITE,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300, .new.err = err_access },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0010,
+		  .orig.how.upgrade_mask = UPGRADE_NOREAD | UPGRADE_NOWRITE,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700, .new.err = err_access },
+
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0050,
+		  .orig.how.upgrade_mask = UPGRADE_NOWRITE,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500 },
+
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0030,
+		  .orig.how.upgrade_mask = UPGRADE_NOREAD,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300 },
+
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0030,
+		  .orig.how.upgrade_mask = UPGRADE_NOREAD,
+		  .new.how.flags  = O_RDONLY, .new.mode   = 0500, .new.err = err_access },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0050,
+		  .orig.how.upgrade_mask = UPGRADE_NOWRITE,
+		  .new.how.flags  = O_WRONLY, .new.mode   = 0300, .new.err = err_access },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0030,
+		  .orig.how.upgrade_mask = UPGRADE_NOREAD,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700, .new.err = err_access },
+		{ .open = sys_openat2,  .chmod_mode = 0000,
+		  .orig.how.flags = O_PATH, .orig.mode = 0050,
+		  .orig.how.upgrade_mask = UPGRADE_NOWRITE,
+		  .new.how.flags  =   O_RDWR, .new.mode   = 0700, .new.err = err_access },
+	};
+
+	for (int i = 0; i < ARRAY_LEN(tests); i++) {
+		int fd;
+		char *orig_flagset, *new_flagset;
+		struct reopen_test *test = &tests[i];
+		void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
+
+		E_chmod(tmpfile, test->chmod_mode);
+
+		fd = test->open(AT_FDCWD, tmpfile, &test->orig.how);
+		E_assert(fd >= 0, "open '%s' failed: %m\n", tmpfile);
+
+		/* Make sure that any EACCES we see is not from inode permissions. */
+		E_chmod(tmpfile, 0777);
+
+		if (reopen(fd, test))
+			resultfn = ksft_test_result_fail;
+
+		close(fd);
+
+		new_flagset = openat_flags(test->new.how.flags);
+		if (test->open == sys_openat)
+			orig_flagset = openat_flags(test->orig.how.flags);
+		else if (test->open == sys_openat2)
+			orig_flagset = openat2_flags(&test->orig.how);
+		else
+			ksft_exit_fail_msg("unknown test->open\n");
+
+		resultfn("%sordinary reopen of (orig[%s]=%s, new=%s) chmod=%.3o %s\n",
+			 privileged ? "privileged " : "",
+			 test->open == sys_openat ? "openat" : "openat2",
+			 orig_flagset, new_flagset, test->chmod_mode,
+			 test->new.err < 0 ? strerror(-test->new.err) : "works");
+		fflush(stdout);
+
+		free(new_flagset);
+		free(orig_flagset);
+	}
+
+	unlink(tmpfile);
+}
+
+void test_openat2_cloexec_test(void)
+{
+	void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
+	struct open_how how = {
+		.flags = O_CLOEXEC | O_PATH | O_DIRECTORY,
+	};
+
+	int fd = sys_openat2(AT_FDCWD, ".", &how);
+	E_assert(fd >= 0, "open '.' failed: %m\n");
+
+	int flags = fcntl(fd, F_GETFD);
+	E_assert(flags >= 0, "F_GETFD failed: %m\n");
+
+	if (!(flags & FD_CLOEXEC))
+		resultfn = ksft_test_result_fail;
+
+	resultfn("openat2(O_CLOEXEC) works as expected\n");
+}
+
+int main(int argc, char **argv)
+{
+	bool privileged;
+
+	ksft_print_header();
+	test_openat2_supported();
+
+	/*
+	 * Technically we should be checking CAP_DAC_OVERRIDE, but it's easier to
+	 * just assume that euid=0 has the full capability set.
+	 */
+	privileged = (geteuid() == 0);
+	if (!privileged)
+		ksft_test_result_skip("privileged tests require euid == 0\n");
+	else {
+		test_reopen_ordinary(privileged);
+
+		E_setresuid(65534, 65534, 65534);
+		privileged = (geteuid() == 0);
+	}
+
+	test_reopen_ordinary(privileged);
+	test_openat2_cloexec_test();
+
+	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
+		ksft_exit_fail();
+	else
+		ksft_exit_pass();
+}
diff --git a/tools/testing/selftests/openat2/rename_attack_test.c b/tools/testing/selftests/openat2/rename_attack_test.c
new file mode 100644
index 000000000000..b5e2a68609f1
--- /dev/null
+++ b/tools/testing/selftests/openat2/rename_attack_test.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author: Aleksa Sarai <cyphar@cyphar.com>
+ * Copyright (C) 2018-2019 SUSE LLC.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <syscall.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "../kselftest.h"
+#include "helpers.h"
+
+/* Construct a test directory with the following structure:
+ *
+ * root/
+ * |-- a/
+ * |   `-- c/
+ * `-- b/
+ */
+int setup_testdir(void)
+{
+	int dfd;
+	char dirname[] = "/tmp/ksft-openat2-rename-attack.XXXXXX";
+
+	/* Make the top-level directory. */
+	if (!mkdtemp(dirname))
+		ksft_exit_fail_msg("setup_testdir: failed to create tmpdir\n");
+	dfd = open(dirname, O_PATH | O_DIRECTORY);
+	if (dfd < 0)
+		ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
+
+	E_mkdirat(dfd, "a", 0755);
+	E_mkdirat(dfd, "b", 0755);
+	E_mkdirat(dfd, "a/c", 0755);
+
+	return dfd;
+}
+
+/* Swap @dirfd/@a and @dirfd/@b constantly. Parent must kill this process. */
+pid_t spawn_attack(int dirfd, char *a, char *b)
+{
+	pid_t child = fork();
+	if (child != 0)
+		return child;
+
+	/* If the parent (the test process) dies, kill ourselves too. */
+	prctl(PR_SET_PDEATHSIG, SIGKILL);
+
+	/* Swap @a and @b. */
+	for (;;)
+		renameat2(dirfd, a, dirfd, b, RENAME_EXCHANGE);
+	exit(1);
+}
+
+#define ROUNDS 400000
+void test_rename_attack(void)
+{
+	int dfd, afd, escaped_count = 0;
+	void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
+	pid_t child;
+
+	dfd = setup_testdir();
+	afd = openat(dfd, "a", O_PATH);
+	if (afd < 0)
+		ksft_exit_fail_msg("test_rename_attack: failed to open 'a'\n");
+
+	child = spawn_attack(dfd, "a/c", "b");
+
+	for (int i = 0; i < ROUNDS; i++) {
+		int fd;
+		bool failed;
+		struct open_how how = {
+			.flags = O_PATH,
+			.resolve = RESOLVE_IN_ROOT,
+		};
+		char *victim_path = "c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../..";
+
+		fd = sys_openat2(afd, victim_path, &how);
+		if (fd < 0)
+			failed = (fd != -EXDEV);
+		else
+			failed = !fdequal(fd, afd, NULL);
+
+		escaped_count += failed;
+		close(fd);
+	}
+
+	if (escaped_count > 0)
+		resultfn = ksft_test_result_fail;
+
+	resultfn("rename attack fails (expected 0 breakouts in %d runs, got %d)\n",
+		 ROUNDS, escaped_count);
+
+	/* Should be killed anyway, but might as well make sure. */
+	kill(child, SIGKILL);
+}
+
+int main(int argc, char **argv)
+{
+	ksft_print_header();
+	test_openat2_supported();
+
+	test_rename_attack();
+
+	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
+		ksft_exit_fail();
+	else
+		ksft_exit_pass();
+}
diff --git a/tools/testing/selftests/openat2/resolve_test.c b/tools/testing/selftests/openat2/resolve_test.c
new file mode 100644
index 000000000000..5a9b478c9295
--- /dev/null
+++ b/tools/testing/selftests/openat2/resolve_test.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author: Aleksa Sarai <cyphar@cyphar.com>
+ * Copyright (C) 2018-2019 SUSE LLC.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "../kselftest.h"
+#include "helpers.h"
+
+/*
+ * Construct a test directory with the following structure:
+ *
+ * root/
+ * |-- procexe -> /proc/self/exe
+ * |-- procroot -> /proc/self/root
+ * |-- root/
+ * |-- mnt/ [mountpoint]
+ * |   |-- self -> ../mnt/
+ * |   `-- absself -> /mnt/
+ * |-- etc/
+ * |   `-- passwd
+ * |-- creatlink -> /newfile3
+ * |-- relsym -> etc/passwd
+ * |-- abssym -> /etc/passwd
+ * |-- abscheeky -> /cheeky
+ * |-- abscheeky -> /cheeky
+ * `-- cheeky/
+ *     |-- absself -> /
+ *     |-- self -> ../../root/
+ *     |-- garbageself -> /../../root/
+ *     |-- passwd -> ../cheeky/../cheeky/../etc/../etc/passwd
+ *     |-- abspasswd -> /../cheeky/../cheeky/../etc/../etc/passwd
+ *     |-- dotdotlink -> ../../../../../../../../../../../../../../etc/passwd
+ *     `-- garbagelink -> /../../../../../../../../../../../../../../etc/passwd
+ */
+int setup_testdir(void)
+{
+	int dfd, tmpfd;
+	char dirname[] = "/tmp/ksft-openat2-testdir.XXXXXX";
+
+	/* Unshare and make /tmp a new directory. */
+	E_unshare(CLONE_NEWNS);
+	E_mount("", "/tmp", "", MS_PRIVATE, "");
+
+	/* Make the top-level directory. */
+	if (!mkdtemp(dirname))
+		ksft_exit_fail_msg("setup_testdir: failed to create tmpdir\n");
+	dfd = open(dirname, O_PATH | O_DIRECTORY);
+	if (dfd < 0)
+		ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
+
+	/* A sub-directory which is actually used for tests. */
+	E_mkdirat(dfd, "root", 0755);
+	tmpfd = openat(dfd, "root", O_PATH | O_DIRECTORY);
+	if (tmpfd < 0)
+		ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
+	close(dfd);
+	dfd = tmpfd;
+
+	E_symlinkat("/proc/self/exe", dfd, "procexe");
+	E_symlinkat("/proc/self/root", dfd, "procroot");
+	E_mkdirat(dfd, "root", 0755);
+
+	/* There is no mountat(2), so use chdir. */
+	E_mkdirat(dfd, "mnt", 0755);
+	E_fchdir(dfd);
+	E_mount("tmpfs", "./mnt", "tmpfs", MS_NOSUID | MS_NODEV, "");
+	E_symlinkat("../mnt/", dfd, "mnt/self");
+	E_symlinkat("/mnt/", dfd, "mnt/absself");
+
+	E_mkdirat(dfd, "etc", 0755);
+	E_touchat(dfd, "etc/passwd");
+
+	E_symlinkat("/newfile3", dfd, "creatlink");
+	E_symlinkat("etc/passwd", dfd, "relsym");
+	E_symlinkat("/etc/passwd", dfd, "abssym");
+	E_symlinkat("/cheeky", dfd, "abscheeky");
+
+	E_mkdirat(dfd, "cheeky", 0755);
+
+	E_symlinkat("/", dfd, "cheeky/absself");
+	E_symlinkat("../../root/", dfd, "cheeky/self");
+	E_symlinkat("/../../root/", dfd, "cheeky/garbageself");
+
+	E_symlinkat("../cheeky/../etc/../etc/passwd", dfd, "cheeky/passwd");
+	E_symlinkat("/../cheeky/../etc/../etc/passwd", dfd, "cheeky/abspasswd");
+
+	E_symlinkat("../../../../../../../../../../../../../../etc/passwd",
+		    dfd, "cheeky/dotdotlink");
+	E_symlinkat("/../../../../../../../../../../../../../../etc/passwd",
+		    dfd, "cheeky/garbagelink");
+
+	return dfd;
+}
+
+struct basic_test {
+	const char *dir;
+	const char *path;
+	struct open_how how;
+	bool pass;
+	union {
+		int err;
+		const char *path;
+	} out;
+};
+
+void test_openat2_opath_tests(void)
+{
+	int rootfd;
+	char *procselfexe;
+
+	E_asprintf(&procselfexe, "/proc/%d/exe", getpid());
+	rootfd = setup_testdir();
+
+	struct basic_test tests[] = {
+		/** RESOLVE_BENEATH **/
+		/* Attempts to cross dirfd should be blocked. */
+		{ .path = "/",			.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "cheeky/absself",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "abscheeky/absself",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "..",			.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "../root/",		.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "cheeky/self",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "abscheeky/self",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "cheeky/garbageself",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "abscheeky/garbageself", .how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		/* Only relative paths that stay inside dirfd should work. */
+		{ .path = "root",		.how.resolve = RESOLVE_BENEATH,
+		  .out.path = "root",		.pass = true },
+		{ .path = "etc",		.how.resolve = RESOLVE_BENEATH,
+		  .out.path = "etc",		.pass = true },
+		{ .path = "etc/passwd",		.how.resolve = RESOLVE_BENEATH,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "relsym",		.how.resolve = RESOLVE_BENEATH,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "cheeky/passwd",	.how.resolve = RESOLVE_BENEATH,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "abscheeky/passwd",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "abssym",		.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "/etc/passwd",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "cheeky/abspasswd",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "abscheeky/abspasswd", .how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		/* Tricky paths should fail. */
+		{ .path = "cheeky/dotdotlink",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "abscheeky/dotdotlink", .how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "cheeky/garbagelink",	.how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_BENEATH,
+		  .out.err = -EXDEV,		.pass = false },
+
+		/** RESOLVE_IN_ROOT **/
+		/* All attempts to cross the dirfd will be scoped-to-root. */
+		{ .path = "/",			.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = NULL,		.pass = true },
+		{ .path = "cheeky/absself",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = NULL,		.pass = true },
+		{ .path = "abscheeky/absself",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = NULL,		.pass = true },
+		{ .path = "..",			.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = NULL,		.pass = true },
+		{ .path = "../root/",		.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "root",		.pass = true },
+		{ .path = "../root/",		.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "root",		.pass = true },
+		{ .path = "cheeky/self",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "root",		.pass = true },
+		{ .path = "cheeky/garbageself",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "root",		.pass = true },
+		{ .path = "abscheeky/garbageself", .how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "root",		.pass = true },
+		{ .path = "root",		.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "root",		.pass = true },
+		{ .path = "etc",		.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc",		.pass = true },
+		{ .path = "etc/passwd",		.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "relsym",		.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "cheeky/passwd",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "abscheeky/passwd",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "abssym",		.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "/etc/passwd",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "cheeky/abspasswd",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "abscheeky/abspasswd",.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "cheeky/dotdotlink",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "abscheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "/../../../../abscheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "cheeky/garbagelink",	.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		{ .path = "/../../../../abscheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "etc/passwd",	.pass = true },
+		/* O_CREAT should handle trailing symlinks correctly. */
+		{ .path = "newfile1",		.how.flags = O_CREAT,
+						.how.mode = 0700,
+						.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "newfile1",	.pass = true },
+		{ .path = "/newfile2",		.how.flags = O_CREAT,
+						.how.mode = 0700,
+						.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "newfile2",	.pass = true },
+		{ .path = "/creatlink",		.how.flags = O_CREAT,
+						.how.mode = 0700,
+						.how.resolve = RESOLVE_IN_ROOT,
+		  .out.path = "newfile3",	.pass = true },
+
+		/** RESOLVE_NO_XDEV **/
+		/* Crossing *down* into a mountpoint is disallowed. */
+		{ .path = "mnt",		.how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "mnt/",		.how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "mnt/.",		.how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		/* Crossing *up* out of a mountpoint is disallowed. */
+		{ .dir = "mnt", .path = ".",	.how.resolve = RESOLVE_NO_XDEV,
+		  .out.path = "mnt",		.pass = true },
+		{ .dir = "mnt", .path = "..",	.how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .dir = "mnt", .path = "../mnt", .how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .dir = "mnt", .path = "self",	.how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .dir = "mnt", .path = "absself", .how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		/* Jumping to "/" is ok, but later components cannot cross. */
+		{ .dir = "mnt", .path = "/",	.how.resolve = RESOLVE_NO_XDEV,
+		  .out.path = "/",		.pass = true },
+		{ .dir = "/", .path = "/",	.how.resolve = RESOLVE_NO_XDEV,
+		  .out.path = "/",		.pass = true },
+		{ .path = "/proc/1",		.how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+		{ .path = "/tmp",		.how.resolve = RESOLVE_NO_XDEV,
+		  .out.err = -EXDEV,		.pass = false },
+
+		/** RESOLVE_NO_MAGICLINKS **/
+		/* Regular symlinks should work. */
+		{ .path = "relsym",		.how.resolve = RESOLVE_NO_MAGICLINKS,
+		  .out.path = "etc/passwd",	.pass = true },
+		/* Magic-links should not work. */
+		{ .path = "procexe",		.how.resolve = RESOLVE_NO_MAGICLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "/proc/self/exe",	.how.resolve = RESOLVE_NO_MAGICLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "procroot/etc",	.how.resolve = RESOLVE_NO_MAGICLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "/proc/self/root/etc", .how.resolve = RESOLVE_NO_MAGICLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "/proc/self/root/etc", .how.flags = O_NOFOLLOW,
+						 .how.resolve = RESOLVE_NO_MAGICLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "/proc/self/exe",	.how.flags = O_NOFOLLOW,
+						.how.resolve = RESOLVE_NO_MAGICLINKS,
+		  .out.path = procselfexe,	.pass = true },
+
+		/** RESOLVE_NO_SYMLINKS **/
+		/* Normal paths should work. */
+		{ .path = ".",			.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.path = NULL,		.pass = true },
+		{ .path = "root",		.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.path = "root",		.pass = true },
+		{ .path = "etc",		.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.path = "etc",		.pass = true },
+		{ .path = "etc/passwd",		.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.path = "etc/passwd",	.pass = true },
+		/* Regular symlinks are blocked. */
+		{ .path = "relsym",		.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "abssym",		.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "cheeky/garbagelink",	.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "abscheeky/absself",	.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		/* Trailing symlinks with NO_FOLLOW. */
+		{ .path = "relsym",		.how.flags = O_NOFOLLOW,
+						.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.path = "relsym",		.pass = true },
+		{ .path = "abssym",		.how.flags = O_NOFOLLOW,
+						.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.path = "abssym",		.pass = true },
+		{ .path = "cheeky/garbagelink",	.how.flags = O_NOFOLLOW,
+						.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.path = "cheeky/garbagelink", .pass = true },
+		{ .path = "abscheeky/garbagelink", .how.flags = O_NOFOLLOW,
+						   .how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+		{ .path = "abscheeky/absself",	.how.flags = O_NOFOLLOW,
+						.how.resolve = RESOLVE_NO_SYMLINKS,
+		  .out.err = -ELOOP,		.pass = false },
+	};
+
+	for (int i = 0; i < ARRAY_LEN(tests); i++) {
+		int dfd, fd;
+		bool failed;
+		void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
+		struct basic_test *test = &tests[i];
+		char *flagstr;
+
+		/* Auto-set O_PATH. */
+		if (!(test->how.flags & O_CREAT))
+			test->how.flags |= O_PATH;
+		flagstr = openat2_flags(&test->how);
+
+		if (test->dir)
+			dfd = openat(rootfd, test->dir, O_PATH | O_DIRECTORY);
+		else
+			dfd = dup(rootfd);
+		if (dfd < 0) {
+			resultfn = ksft_test_result_error;
+			goto next;
+		}
+
+		fd = sys_openat2(dfd, test->path, &test->how);
+		if (test->pass)
+			failed = (fd < 0 || !fdequal(fd, rootfd, test->out.path));
+		else
+			failed = (fd != test->out.err);
+		if (fd >= 0)
+			close(fd);
+		close(dfd);
+
+		if (failed)
+			resultfn = ksft_test_result_fail;
+
+next:
+		if (test->pass)
+			resultfn("openat2(root[%s], %s, %s) ==> %s\n",
+				 test->dir ?: ".", test->path, flagstr,
+				 test->out.path ?: ".");
+		else
+			resultfn("openat2(root[%s], %s, %s) ==> %d (%s)\n",
+				 test->dir ?: ".", test->path, flagstr,
+				 test->out.err, strerror(-test->out.err));
+		fflush(stdout);
+
+		free(flagstr);
+	}
+
+	free(procselfexe);
+	close(rootfd);
+}
+
+int main(int argc, char **argv)
+{
+	ksft_print_header();
+	test_openat2_supported();
+
+	/* NOTE: We should be checking for CAP_SYS_ADMIN here... */
+	if (geteuid() != 0)
+		ksft_exit_skip("openat2(2) tests require euid == 0\n");
+
+	test_openat2_opath_tests();
+
+	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
+		ksft_exit_fail();
+	else
+		ksft_exit_pass();
+}
-- 
2.22.0



^ permalink raw reply related

* Re: [PATCH v10 8/9] kselftest: save-and-restore errno to allow for %m formatting
From: shuah @ 2019-07-19 22:25 UTC (permalink / raw)
  To: Aleksa Sarai, Al Viro, Jeff Layton, J. Bruce Fields,
	Arnd Bergmann, David Howells, Shuah Khan
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, Oleg Nesterov,
	linux-kselftest, sparclinux, shuah, linux-arch, linux-s390,
	Tycho Andersen, Aleksa Sarai, linux-arm-kernel, linux-mips,
	linux-xtensa, Kees Cook, Jann Horn, linuxppc-dev, linux-m68k,
	Andy Lutomirski, David Drysdale, Christian Brauner, linux-parisc,
	linux-api, Chanho Min, linux-kernel, Eric Biederman, linux-alpha,
	linux-fsdevel, Andrew Morton, Linus Torvalds, containers
In-Reply-To: <20190719164225.27083-9-cyphar@cyphar.com>

On 7/19/19 10:42 AM, Aleksa Sarai wrote:
> Previously, using "%m" in a ksft_* format string can result in strange
> output because the errno value wasn't saved before calling other libc
> functions. The solution is to simply save and restore the errno before
> we format the user-supplied format string.
> 
> Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
> ---
>   tools/testing/selftests/kselftest.h | 15 +++++++++++++++
>   1 file changed, 15 insertions(+)
> 
> diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h
> index ec15c4f6af55..0ac49d91a260 100644
> --- a/tools/testing/selftests/kselftest.h
> +++ b/tools/testing/selftests/kselftest.h
> @@ -10,6 +10,7 @@
>   #ifndef __KSELFTEST_H
>   #define __KSELFTEST_H
>   
> +#include <errno.h>
>   #include <stdlib.h>
>   #include <unistd.h>
>   #include <stdarg.h>
> @@ -81,58 +82,68 @@ static inline void ksft_print_cnts(void)
>   
>   static inline void ksft_print_msg(const char *msg, ...)
>   {
> +	int saved_errno = errno;
>   	va_list args;
>   
>   	va_start(args, msg);
>   	printf("# ");
> +	errno = saved_errno;
>   	vprintf(msg, args);
>   	va_end(args);
>   }
>   
>   static inline void ksft_test_result_pass(const char *msg, ...)
>   {
> +	int saved_errno = errno;
>   	va_list args;
>   
>   	ksft_cnt.ksft_pass++;
>   
>   	va_start(args, msg);
>   	printf("ok %d ", ksft_test_num());
> +	errno = saved_errno;
>   	vprintf(msg, args);
>   	va_end(args);
>   }
>   
>   static inline void ksft_test_result_fail(const char *msg, ...)
>   {
> +	int saved_errno = errno;
>   	va_list args;
>   
>   	ksft_cnt.ksft_fail++;
>   
>   	va_start(args, msg);
>   	printf("not ok %d ", ksft_test_num());
> +	errno = saved_errno;
>   	vprintf(msg, args);
>   	va_end(args);
>   }
>   
>   static inline void ksft_test_result_skip(const char *msg, ...)
>   {
> +	int saved_errno = errno;
>   	va_list args;
>   
>   	ksft_cnt.ksft_xskip++;
>   
>   	va_start(args, msg);
>   	printf("not ok %d # SKIP ", ksft_test_num());
> +	errno = saved_errno;
>   	vprintf(msg, args);
>   	va_end(args);
>   }
>   
>   static inline void ksft_test_result_error(const char *msg, ...)
>   {
> +	int saved_errno = errno;
>   	va_list args;
>   
>   	ksft_cnt.ksft_error++;
>   
>   	va_start(args, msg);
>   	printf("not ok %d # error ", ksft_test_num());
> +	errno = saved_errno;
>   	vprintf(msg, args);
>   	va_end(args);
>   }
> @@ -152,10 +163,12 @@ static inline int ksft_exit_fail(void)
>   
>   static inline int ksft_exit_fail_msg(const char *msg, ...)
>   {
> +	int saved_errno = errno;
>   	va_list args;
>   
>   	va_start(args, msg);
>   	printf("Bail out! ");
> +	errno = saved_errno;
>   	vprintf(msg, args);
>   	va_end(args);
>   
> @@ -178,10 +191,12 @@ static inline int ksft_exit_xpass(void)
>   static inline int ksft_exit_skip(const char *msg, ...)
>   {
>   	if (msg) {
> +		int saved_errno = errno;
>   		va_list args;
>   
>   		va_start(args, msg);
>   		printf("not ok %d # SKIP ", 1 + ksft_test_num());
> +		errno = saved_errno;
>   		vprintf(msg, args);
>   		va_end(args);
>   	} else {
> 

Hi Aleksa,

Can you send this patch separate from the patch series. I will apply
this as bug fix to 5.3-rc2 or rc3.

This isn't part of this series anyway and I would like to get this in
right away.

thanks,
-- Shuah

^ permalink raw reply

* Re: [PATCH v10 8/9] kselftest: save-and-restore errno to allow for %m formatting
From: Aleksa Sarai @ 2019-07-20  0:09 UTC (permalink / raw)
  To: shuah
  Cc: linux-ia64, linux-sh, Alexei Starovoitov, linux-kernel,
	David Howells, linux-kselftest, sparclinux, linux-arch,
	linux-s390, Tycho Andersen, Aleksa Sarai, linux-arm-kernel,
	linux-mips, linux-xtensa, Kees Cook, Arnd Bergmann, Jann Horn,
	linuxppc-dev, linux-m68k, Al Viro, Andy Lutomirski, Shuah Khan,
	David Drysdale, Christian Brauner, J. Bruce Fields, linux-parisc,
	linux-api, Chanho Min, Jeff Layton, Oleg Nesterov, Eric Biederman,
	linux-alpha, linux-fsdevel, Andrew Morton, Linus Torvalds,
	containers
In-Reply-To: <b32d95a1-8a49-65ef-4ddd-fe86a7ca01d5@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 822 bytes --]

On 2019-07-19, shuah <shuah@kernel.org> wrote:
> On 7/19/19 10:42 AM, Aleksa Sarai wrote:
> > Previously, using "%m" in a ksft_* format string can result in strange
> > output because the errno value wasn't saved before calling other libc
> > functions. The solution is to simply save and restore the errno before
> > we format the user-supplied format string.
> > 
> > Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
> [...]
> Hi Aleksa,
> 
> Can you send this patch separate from the patch series. I will apply
> this as bug fix to 5.3-rc2 or rc3.
> 
> This isn't part of this series anyway and I would like to get this in
> right away.

Done, and I'll drop it in v11 after the rest gets reviewed.

-- 
Aleksa Sarai
Senior Software Engineer (Containers)
SUSE Linux GmbH
<https://www.cyphar.com/>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH 2/3] DMA mapping: Move SME handling to x86-specific files
From: Thiago Jung Bauermann @ 2019-07-20  0:22 UTC (permalink / raw)
  To: kbuild test robot
  Cc: linux-s390, Mike Anderson, Konrad Rzeszutek Wilk, Robin Murphy,
	x86, Ram Pai, linux-kernel, Alexey Dobriyan, Halil Pasic, iommu,
	Ingo Molnar, Borislav Petkov, kbuild-all, H. Peter Anvin,
	linux-fsdevel, Thomas Gleixner, linuxppc-dev, Christoph Hellwig,
	Marek Szyprowski
In-Reply-To: <201907191711.8BlpwBo2%lkp@intel.com>


kbuild test robot <lkp@intel.com> writes:

> Hi Thiago,
>
> Thank you for the patch! Yet something to improve:
>
> [auto build test ERROR on linus/master]
> [cannot apply to v5.2 next-20190718]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
>
> url:    https://github.com/0day-ci/linux/commits/Thiago-Jung-Bauermann/Remove-x86-specific-code-from-generic-headers/20190715-063006
> config: s390-allnoconfig (attached as .config)
> compiler: s390-linux-gcc (GCC) 7.4.0
> reproduce:
>         wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # save the attached .config to linux build tree
>         GCC_VERSION=7.4.0 make.cross ARCH=s390
>
> If you fix the issue, kindly add following tag
> Reported-by: kbuild test robot <lkp@intel.com>
>
> All errors (new ones prefixed by >>):
>
>    kernel/dma/swiotlb.c: In function 'swiotlb_tbl_map_single':
>>> kernel/dma/swiotlb.c:461:6: error: implicit declaration of function 'mem_encrypt_active'; did you mean 'set_cpu_active'? [-Werror=implicit-function-declaration]
>      if (mem_encrypt_active())
>          ^~~~~~~~~~~~~~~~~~
>          set_cpu_active
>    cc1: some warnings being treated as errors

This error was reported for v1 of the patch series. I wasn't able to
reproduce this problem on v1 but found a similar issue on v2.

I just did a build test of each patch of the latest version (v3) with an
s390 cross-toolchain and the config file from this report and didn't
find any build issues, so I believe this problem is solved.

--
Thiago Jung Bauermann
IBM Linux Technology Center

^ permalink raw reply

* [PATCH kernel RFC 2/2] powerpc/pseries: Kexec style ibm, client-architecture-support support
From: Alexey Kardashevskiy @ 2019-07-20  1:29 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Alexey Kardashevskiy, kvm-ppc, David Gibson
In-Reply-To: <20190720012919.14417-1-aik@ozlabs.ru>

This checks the FDT for "/chosen/qemu,h_cas" and calls H_CAS when present.
The H_CAS hcall is implemented in QEMU for ages and currently returns
an FDT with a diff to the initial FDT which SLOF updates and returns to
the OS. For this patch to work, QEMU needs to provide the full tree
instead, so when QEMU is run with the "-machine pseries,bios=no",
it reads the existing FDT from the OS, updats it and writes back on top.

This changes prom_check_platform_support() not to call the client
interface's prom_getproplen() as the kexec-style boot does not provide
the client interface services.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 arch/powerpc/kernel/prom_init.c | 12 ++++++----
 arch/powerpc/kernel/setup_64.c  | 41 +++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 514707ef6779..6d8e35cb3c57 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -1261,7 +1261,7 @@ static void __init prom_parse_platform_support(u8 index, u8 val,
 	}
 }
 
-static void __init prom_check_platform_support(void)
+struct ibm_arch_vec __init *prom_check_platform_support(int prop_len)
 {
 	struct platform_support supported = {
 		.hash_mmu = false,
@@ -1269,8 +1269,6 @@ static void __init prom_check_platform_support(void)
 		.radix_gtse = false,
 		.xive = false
 	};
-	int prop_len = prom_getproplen(prom.chosen,
-				       "ibm,arch-vec-5-platform-support");
 
 	/*
 	 * First copy the architecture vec template
@@ -1319,7 +1317,12 @@ static void __init prom_check_platform_support(void)
 		prom_debug("Asking for XIVE\n");
 		ibm_architecture_vec.vec5.intarch = OV5_FEAT(OV5_XIVE_EXPLOIT);
 	}
+
+	ibm_architecture_vec.vec5.max_cpus = cpu_to_be32(NR_CPUS);
+
+	return &ibm_architecture_vec;
 }
+EXPORT_SYMBOL_GPL(prom_check_platform_support);
 
 static void __init prom_send_capabilities(void)
 {
@@ -1328,7 +1331,8 @@ static void __init prom_send_capabilities(void)
 	u32 cores;
 
 	/* Check ibm,arch-vec-5-platform-support and fixup vec5 if required */
-	prom_check_platform_support();
+	prom_check_platform_support(prom_getproplen(prom.chosen,
+				"ibm,arch-vec-5-platform-support"));
 
 	root = call_prom("open", 1, 1, ADDR("/"));
 	if (root != 0) {
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index 44b4c432a273..6fa384278180 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -284,12 +284,53 @@ void __init record_spr_defaults(void)
  * device-tree is not accessible via normal means at this point.
  */
 
+/*
+ * The architecture vector has an array of PVR mask/value pairs,
+ * followed by # option vectors - 1, followed by the option vectors.
+ *
+ * See prom.h for the definition of the bits specified in the
+ * architecture vector.
+ */
+
+extern struct ibm_arch_vec __init *prom_check_platform_support(
+		int vec5_prop_len);
+
+int __init early_init_dt_scan_chosen_h_cas(unsigned long node,
+		const char *uname, int depth, void *data)
+{
+	int l;
+	const char *p;
+
+	if (depth != 1 || !data ||
+	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
+		return 0;
+	p = of_get_flat_dt_prop(node, "qemu,h_cas", &l);
+	if (p != NULL && l > 0)
+		*(bool *) data = be32_to_cpu(*(uint32_t *) p) != 0;
+
+	return 1;
+}
+
 void __init early_setup(unsigned long dt_ptr)
 {
 	static __initdata struct paca_struct boot_paca;
+	struct ibm_arch_vec *vec = prom_check_platform_support(0);
 
 	/* -------- printk is _NOT_ safe to use here ! ------- */
 
+	/* ibm,client-architecture-support support */
+#define KVMPPC_HCALL_BASE       0xf000
+#define KVMPPC_H_CAS            (KVMPPC_HCALL_BASE + 0x2)
+#define FDT_MAX_SIZE 0x100000
+	bool do_h_cas = false;
+
+	if (early_init_dt_verify(__va(dt_ptr))) {
+		of_scan_flat_dt(early_init_dt_scan_chosen_h_cas, &do_h_cas);
+		if (do_h_cas)
+			plpar_hcall_norets(KVMPPC_H_CAS, vec, dt_ptr,
+					FDT_MAX_SIZE);
+	}
+
 	/* Try new device tree based feature discovery ... */
 	if (!dt_cpu_ftrs_init(__va(dt_ptr)))
 		/* Otherwise use the old style CPU table */
-- 
2.17.1


^ permalink raw reply related

* [PATCH kernel RFC 0/2] powerpc/pseries: Kexec style boot
From: Alexey Kardashevskiy @ 2019-07-20  1:29 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Alexey Kardashevskiy, kvm-ppc, Paul Mackerras, David Gibson

There is a funny excercise to run a guest under QEMU without
the SLOF firmware and boot into a kernel directly to use petitboot as
a boot loader (a more power boot loader than grub and yum),
the patchset is posted as "spapr: Kexec style boot".

Since there is no SLOF, i.e. no client interface and no RTAS blob,
we need to avoid the former and call the latter directly. Also,
this implements "client-architecture-support" substiitute for
the new environment.

This is based on sha1
a2b6f26c264e Christophe Leroy "powerpc/module64: Use symbolic instructions names.".

Please comment. Thanks.



Alexey Kardashevskiy (2):
  powerpc/pseries: Call RTAS directly
  powerpc/pseries: Kexec style ibm,client-architecture-support support

 arch/powerpc/include/asm/rtas.h |  1 +
 arch/powerpc/kernel/prom_init.c | 12 ++++++---
 arch/powerpc/kernel/rtas.c      | 47 +++++++++++++++------------------
 arch/powerpc/kernel/setup_64.c  | 41 ++++++++++++++++++++++++++++
 4 files changed, 71 insertions(+), 30 deletions(-)

-- 
2.17.1


^ permalink raw reply

* [PATCH kernel RFC 1/2] powerpc/pseries: Call RTAS directly
From: Alexey Kardashevskiy @ 2019-07-20  1:29 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Alexey Kardashevskiy, kvm-ppc, David Gibson
In-Reply-To: <20190720012919.14417-1-aik@ozlabs.ru>

The pseries guests call RTAS via a RTAS entry point which is a firmware
image under powernv and simple HCALL wrapper under QEMU. For the latter,
we can skip the binary image and do HCALL directly, eliminating the need
in the RTAS blob entirely.

This checks the DT whether the new method is supported and use it if it is.
This removes few checks as QEMU might decide not to keen RTAS around at
all (might be the case for secure VMs).

Note that kexec still checks for the linux,rtas-xxx properties which has
to be fixed.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 arch/powerpc/include/asm/rtas.h |  1 +
 arch/powerpc/kernel/rtas.c      | 47 +++++++++++++++------------------
 2 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index 3c1887351c71..60cd528806c1 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -59,6 +59,7 @@ struct rtas_t {
 	arch_spinlock_t lock;
 	struct rtas_args args;
 	struct device_node *dev;	/* virtual address pointer */
+	bool hcall;
 };
 
 struct rtas_suspend_me_data {
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 5faf0a64c92b..0651291ab5ff 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -49,6 +49,14 @@ struct rtas_t rtas = {
 };
 EXPORT_SYMBOL(rtas);
 
+static void __enter_rtas(unsigned long pa)
+{
+	if (rtas.hcall)
+		plpar_hcall_norets(H_RTAS, pa);
+	else
+		enter_rtas(pa);
+}
+
 DEFINE_SPINLOCK(rtas_data_buf_lock);
 EXPORT_SYMBOL(rtas_data_buf_lock);
 
@@ -95,9 +103,6 @@ static void call_rtas_display_status(unsigned char c)
 {
 	unsigned long s;
 
-	if (!rtas.base)
-		return;
-
 	s = lock_rtas();
 	rtas_call_unlocked(&rtas.args, 10, 1, 1, NULL, c);
 	unlock_rtas(s);
@@ -145,9 +150,6 @@ static void udbg_rtascon_putc(char c)
 {
 	int tries;
 
-	if (!rtas.base)
-		return;
-
 	/* Add CRs before LFs */
 	if (c == '\n')
 		udbg_rtascon_putc('\r');
@@ -164,9 +166,6 @@ static int udbg_rtascon_getc_poll(void)
 {
 	int c;
 
-	if (!rtas.base)
-		return -1;
-
 	if (rtas_call(rtas_getchar_token, 0, 2, &c))
 		return -1;
 
@@ -205,9 +204,6 @@ void rtas_progress(char *s, unsigned short hex)
 	static int current_line;
 	static int pending_newline = 0;  /* did last write end with unprinted newline? */
 
-	if (!rtas.base)
-		return;
-
 	if (display_width == 0) {
 		display_width = 0x10;
 		if ((root = of_find_node_by_path("/rtas"))) {
@@ -382,7 +378,7 @@ static char *__fetch_rtas_last_error(char *altbuf)
 	save_args = rtas.args;
 	rtas.args = err_args;
 
-	enter_rtas(__pa(&rtas.args));
+	__enter_rtas(__pa(&rtas.args));
 
 	err_args = rtas.args;
 	rtas.args = save_args;
@@ -428,7 +424,7 @@ va_rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret,
 	for (i = 0; i < nret; ++i)
 		args->rets[i] = 0;
 
-	enter_rtas(__pa(args));
+	__enter_rtas(__pa(args));
 }
 
 void rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, ...)
@@ -449,7 +445,7 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
 	char *buff_copy = NULL;
 	int ret;
 
-	if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE)
+	if (token == RTAS_UNKNOWN_SERVICE)
 		return -1;
 
 	s = lock_rtas();
@@ -1064,9 +1060,6 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	if (!rtas.entry)
-		return -EINVAL;
-
 	if (copy_from_user(&args, uargs, 3 * sizeof(u32)) != 0)
 		return -EFAULT;
 
@@ -1115,7 +1108,7 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
 	flags = lock_rtas();
 
 	rtas.args = args;
-	enter_rtas(__pa(&rtas.args));
+	__enter_rtas(__pa(&rtas.args));
 	args = rtas.args;
 
 	/* A -1 return code indicates that the last command couldn't
@@ -1149,8 +1142,8 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
 void __init rtas_initialize(void)
 {
 	unsigned long rtas_region = RTAS_INSTANTIATE_MAX;
-	u32 base, size, entry;
-	int no_base, no_size, no_entry;
+	u32 base = 0, size = 0, entry = 0, do_h_rtas = 0;
+	int no_base, no_size, no_entry, ret;
 
 	/* Get RTAS dev node and fill up our "rtas" structure with infos
 	 * about it.
@@ -1161,17 +1154,15 @@ void __init rtas_initialize(void)
 
 	no_base = of_property_read_u32(rtas.dev, "linux,rtas-base", &base);
 	no_size = of_property_read_u32(rtas.dev, "rtas-size", &size);
-	if (no_base || no_size) {
-		of_node_put(rtas.dev);
-		rtas.dev = NULL;
-		return;
-	}
 
 	rtas.base = base;
 	rtas.size = size;
 	no_entry = of_property_read_u32(rtas.dev, "linux,rtas-entry", &entry);
 	rtas.entry = no_entry ? rtas.base : entry;
 
+	ret = of_property_read_u32(of_chosen, "qemu,h_rtas", &do_h_rtas);
+	rtas.hcall = !ret && do_h_rtas;
+
 	/* If RTAS was found, allocate the RMO buffer for it and look for
 	 * the stop-self token if any
 	 */
@@ -1208,6 +1199,10 @@ int __init early_init_dt_scan_rtas(unsigned long node,
 		rtas.base = *basep;
 		rtas.entry = *entryp;
 		rtas.size = *sizep;
+	} else {
+		rtas.base = 0;
+		rtas.entry = 0;
+		rtas.size = 0;
 	}
 
 #ifdef CONFIG_UDBG_RTAS_CONSOLE
-- 
2.17.1


^ permalink raw reply related

* Re: question on "powerpc/pseries/dma: Allow SWIOTLB"
From: Alexey Kardashevskiy @ 2019-07-20 11:22 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linuxppc-dev
In-Reply-To: <20190719122518.GA18682@infradead.org>



On 19/07/2019 22:25, Christoph Hellwig wrote:
> On Fri, Jul 19, 2019 at 06:23:59PM +1000, Alexey Kardashevskiy wrote:
>> It is getting there and I still do not see why "swiotlb=force" should not
>> work if chosed in the cmdline.
> 
> Ok, makes sense.  But that means we also have the issue in a few
> other places..

Hmm, where? I got broadcom ethernet working with this.


-- 
Alexey

^ permalink raw reply

* Re: [PATCH v2] powerpc: slightly improve cache helpers
From: Nathan Chancellor @ 2019-07-21  7:58 UTC (permalink / raw)
  To: Segher Boessenkool
  Cc: linux-kernel, clang-built-linux, Paul Mackerras, linuxppc-dev
In-Reply-To: <20190719160455.GA12420@archlinux-threadripper>

[-- Attachment #1: Type: text/plain, Size: 1874 bytes --]

On Fri, Jul 19, 2019 at 09:04:55AM -0700, Nathan Chancellor wrote:
> On Fri, Jul 19, 2019 at 10:23:03AM -0500, Segher Boessenkool wrote:
> > On Thu, Jul 18, 2019 at 08:24:56PM -0700, Nathan Chancellor wrote:
> > > On Mon, Jul 08, 2019 at 11:49:52PM -0700, Nathan Chancellor wrote:
> > > > On Tue, Jul 09, 2019 at 07:04:43AM +0200, Christophe Leroy wrote:
> > > > > Is that a Clang bug ?
> > > > 
> > > > No idea, it happens with clang-8 and clang-9 though (pretty sure there
> > > > were fixes for PowerPC in clang-8 so something before it probably won't
> > > > work but I haven't tried).
> > > > 
> > > > > 
> > > > > Do you have a disassembly of the code both with and without this patch in
> > > > > order to compare ?
> > > > 
> > > > I can give you whatever disassembly you want (or I can upload the raw
> > > > files if that is easier).
> > > 
> > > What disassembly/files did you need to start taking a look at this? I
> > > can upload/send whatever you need.
> > 
> > A before and after of *only this patch*.  And then look at what changed;
> > it maybe be obvious what is the problem to you, as well, so look at it
> > yourself first?
> > 
> > 
> > Segher

Hi Segher,

Looks like the problematic function is dcbz, as there is no segfault
when only that function is reverted to a
pre-6c5875843b87c3adea2beade9d1b8b3d4523900a state.

I was able to expose a singular problematic callsite using the attached
patch (let me know if that is insufficient).

I have attached the disassembly of arch/powerpc/kernel/mem.o with
clear_page (working) and broken_clear_page (broken), along with the side
by side diff. My assembly knowledge is fairly limited as it stands and
it is certainly not up to snuff on PowerPC so I have no idea what I am
looking for. Please let me know if anything immediately looks off or if
there is anything else I can do to help out.

Cheers,
Nathan

[-- Attachment #2: 0001-powerpc-Test-broken-dcbz.patch --]
[-- Type: text/plain, Size: 2103 bytes --]

From 3d7f79f7601c312d47245141185bea7defffffa4 Mon Sep 17 00:00:00 2001
From: Nathan Chancellor <natechancellor@gmail.com>
Date: Sat, 20 Jul 2019 23:37:55 -0700
Subject: [PATCH] powerpc: Test broken dcbz

Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
---
 arch/powerpc/include/asm/cache.h   |  2 +-
 arch/powerpc/include/asm/page_32.h | 13 +++++++++++++
 arch/powerpc/mm/mem.c              |  2 +-
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/include/asm/cache.h b/arch/powerpc/include/asm/cache.h
index b3388d95f451..ba76291b4d4d 100644
--- a/arch/powerpc/include/asm/cache.h
+++ b/arch/powerpc/include/asm/cache.h
@@ -107,7 +107,7 @@ extern void _set_L3CR(unsigned long);
 
 static inline void dcbz(void *addr)
 {
-	__asm__ __volatile__ ("dcbz %y0" : : "Z"(*(u8 *)addr) : "memory");
+	__asm__ __volatile__ ("dcbz 0, %0" : : "r"(addr) : "memory");
 }
 
 static inline void dcbi(void *addr)
diff --git a/arch/powerpc/include/asm/page_32.h b/arch/powerpc/include/asm/page_32.h
index 683dfbc67ca8..125c2ba7cd79 100644
--- a/arch/powerpc/include/asm/page_32.h
+++ b/arch/powerpc/include/asm/page_32.h
@@ -40,6 +40,19 @@ typedef unsigned long long pte_basic_t;
 typedef unsigned long pte_basic_t;
 #endif
 
+static inline void broken_dcbz(void *addr)
+{
+	__asm__ __volatile__ ("dcbz %y0" : : "Z"(*(u8 *)addr) : "memory");
+}
+
+static inline void broken_clear_page(void *addr)
+{
+	unsigned int i;
+
+	for (i = 0; i < PAGE_SIZE / L1_CACHE_BYTES; i++, addr += L1_CACHE_BYTES)
+		broken_dcbz(addr);
+}
+
 /*
  * Clear page using the dcbz instruction, which doesn't cause any
  * memory traffic (except to write out any cache lines which get
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 9259337d7374..7bb88e7a2e4c 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -361,7 +361,7 @@ EXPORT_SYMBOL(flush_dcache_icache_page);
 
 void clear_user_page(void *page, unsigned long vaddr, struct page *pg)
 {
-	clear_page(page);
+	broken_clear_page(page);
 
 	/*
 	 * We shouldn't have to do this, but some versions of glibc
-- 
2.22.0


[-- Attachment #3: mem-working.txt --]
[-- Type: text/plain, Size: 22052 bytes --]


mem-working.o:	file format ELF32-ppc


Disassembly of section .text:

00000000 phys_mem_access_prot:
       0: 7c 08 02 a6                  	mflr 0
       4: 90 01 00 04                  	stw 0, 4(1)
       8: 94 21 ff e0                  	stwu 1, -32(1)
       c: 93 a1 00 14                  	stw 29, 20(1)
      10: 7c fd 3b 78                  	mr 29, 7
      14: 3c e0 00 00                  	lis 7, 0
      18: 38 e7 00 00                  	addi 7, 7, 0
      1c: 81 07 00 a0                  	lwz 8, 160(7)
      20: 93 c1 00 18                  	stw 30, 24(1)
      24: 28 08 00 00                  	cmplwi	8, 0
      28: 7c 7e 1b 78                  	mr 30, 3
      2c: 41 82 00 20                  	bt	2, .+32
      30: 80 7d 00 00                  	lwz 3, 0(29)
      34: 38 e1 00 08                  	addi 7, 1, 8
      38: 7d 09 03 a6                  	mtctr 8
      3c: 90 61 00 08                  	stw 3, 8(1)
      40: 7f c3 f3 78                  	mr 3, 30
      44: 4e 80 04 21                  	bctrl
      48: 48 00 00 28                  	b .+40
      4c: 7c a3 2b 78                  	mr 3, 5
      50: 48 00 00 01                  	bl .+0
      54: 28 03 00 00                  	cmplwi	3, 0
      58: 80 7d 00 00                  	lwz 3, 0(29)
      5c: 40 82 00 10                  	bf	2, .+16
      60: 54 63 06 26                  	rlwinm 3, 3, 0, 24, 19
      64: 60 63 05 00                  	ori 3, 3, 1280
      68: 90 7d 00 00                  	stw 3, 0(29)
      6c: 90 7e 00 00                  	stw 3, 0(30)
      70: 83 c1 00 18                  	lwz 30, 24(1)
      74: 83 a1 00 14                  	lwz 29, 20(1)
      78: 80 01 00 24                  	lwz 0, 36(1)
      7c: 38 21 00 20                  	addi 1, 1, 32
      80: 7c 08 03 a6                  	mtlr 0
      84: 4e 80 00 20                  	blr

00000088 free_initmem:
      88: 7c 08 02 a6                  	mflr 0
      8c: 90 01 00 04                  	stw 0, 4(1)
      90: 94 21 ff f0                  	stwu 1, -16(1)
      94: 3c 60 00 00                  	lis 3, 0
      98: 3c 80 00 00                  	lis 4, 0
      9c: 38 63 00 00                  	addi 3, 3, 0
      a0: 38 84 00 00                  	addi 4, 4, 0
      a4: 90 83 00 60                  	stw 4, 96(3)
      a8: 48 00 00 01                  	bl .+0
      ac: 3c 60 00 00                  	lis 3, 0
      b0: 38 80 00 01                  	li 4, 1
      b4: 98 83 00 00                  	stb 4, 0(3)
      b8: 3c 60 00 00                  	lis 3, 0
      bc: 3c 80 00 00                  	lis 4, 0
      c0: 3c a0 00 00                  	lis 5, 0
      c4: 38 63 00 00                  	addi 3, 3, 0
      c8: 38 84 00 00                  	addi 4, 4, 0
      cc: 38 c5 00 00                  	addi 6, 5, 0
      d0: 38 a0 00 cc                  	li 5, 204
      d4: 48 00 00 01                  	bl .+0
      d8: 80 01 00 14                  	lwz 0, 20(1)
      dc: 38 21 00 10                  	addi 1, 1, 16
      e0: 7c 08 03 a6                  	mtlr 0
      e4: 4e 80 00 20                  	blr

000000e8 flush_dcache_page:
      e8: 80 83 00 00                  	lwz 4, 0(3)
      ec: 70 84 08 00                  	andi. 4, 4, 2048
      f0: 4d 82 00 20                  	bclr	12, 2
      f4: 38 80 08 00                  	li 4, 2048
      f8: 7c a0 18 28                  	lwarx 5, 0, 3
      fc: 7c a5 20 78                  	andc 5, 5, 4
     100: 7c a0 19 2d                  	stwcx. 5, 0, 3
     104: 40 a2 ff f4                  	bc 5, 2, .+65524
     108: 4e 80 00 20                  	blr

0000010c flush_dcache_icache_page:
     10c: 7c 08 02 a6                  	mflr 0
     110: 90 01 00 04                  	stw 0, 4(1)
     114: 94 21 ff f0                  	stwu 1, -16(1)
     118: 80 82 00 00                  	lwz 4, 0(2)
     11c: 38 84 00 01                  	addi 4, 4, 1
     120: 90 82 00 00                  	stw 4, 0(2)
     124: 80 82 0a e4                  	lwz 4, 2788(2)
     128: 38 84 00 01                  	addi 4, 4, 1
     12c: 90 82 0a e4                  	stw 4, 2788(2)
     130: 3c 80 00 00                  	lis 4, 0
     134: 80 84 00 00                  	lwz 4, 0(4)
     138: 7c 64 18 50                  	subf 3, 4, 3
     13c: 3c 80 cc cc                  	lis 4, -13108
     140: 54 63 e8 fe                  	srwi 3, 3, 3
     144: 60 84 d0 00                  	ori 4, 4, 53248
     148: 7c 63 21 d6                  	mullw 3, 3, 4
     14c: 3c 63 c0 00                  	addis 3, 3, -16384
     150: 48 00 00 01                  	bl .+0
     154: 80 62 0a e4                  	lwz 3, 2788(2)
     158: 38 63 ff ff                  	addi 3, 3, -1
     15c: 90 62 0a e4                  	stw 3, 2788(2)
     160: 80 62 00 00                  	lwz 3, 0(2)
     164: 38 63 ff ff                  	addi 3, 3, -1
     168: 90 62 00 00                  	stw 3, 0(2)
     16c: 80 01 00 14                  	lwz 0, 20(1)
     170: 38 21 00 10                  	addi 1, 1, 16
     174: 7c 08 03 a6                  	mtlr 0
     178: 4e 80 00 20                  	blr

0000017c clear_user_page:
     17c: 38 80 00 80                  	li 4, 128
     180: 7c 89 03 a6                  	mtctr 4
     184: 7c 00 1f ec                  	dcbz 0, 3
     188: 38 63 00 20                  	addi 3, 3, 32
     18c: 42 00 ff f8                  	bdnz .+65528
     190: 80 65 00 00                  	lwz 3, 0(5)
     194: 70 63 08 00                  	andi. 3, 3, 2048
     198: 4d 82 00 20                  	bclr	12, 2
     19c: 38 60 08 00                  	li 3, 2048
     1a0: 7c 80 28 28                  	lwarx 4, 0, 5
     1a4: 7c 84 18 78                  	andc 4, 4, 3
     1a8: 7c 80 29 2d                  	stwcx. 4, 0, 5
     1ac: 40 a2 ff f4                  	bc 5, 2, .+65524
     1b0: 4e 80 00 20                  	blr

000001b4 copy_user_page:
     1b4: 7c 08 02 a6                  	mflr 0
     1b8: 90 01 00 04                  	stw 0, 4(1)
     1bc: 94 21 ff f0                  	stwu 1, -16(1)
     1c0: 93 c1 00 08                  	stw 30, 8(1)
     1c4: 7c de 33 78                  	mr 30, 6
     1c8: 48 00 00 01                  	bl .+0
     1cc: 80 7e 00 00                  	lwz 3, 0(30)
     1d0: 70 63 08 00                  	andi. 3, 3, 2048
     1d4: 41 82 00 18                  	bt	2, .+24
     1d8: 38 60 08 00                  	li 3, 2048
     1dc: 7c 80 f0 28                  	lwarx 4, 0, 30
     1e0: 7c 84 18 78                  	andc 4, 4, 3
     1e4: 7c 80 f1 2d                  	stwcx. 4, 0, 30
     1e8: 40 a2 ff f4                  	bc 5, 2, .+65524
     1ec: 83 c1 00 08                  	lwz 30, 8(1)
     1f0: 80 01 00 14                  	lwz 0, 20(1)
     1f4: 38 21 00 10                  	addi 1, 1, 16
     1f8: 7c 08 03 a6                  	mtlr 0
     1fc: 4e 80 00 20                  	blr

00000200 flush_icache_user_range:
     200: 7c 08 02 a6                  	mflr 0
     204: 90 01 00 04                  	stw 0, 4(1)
     208: 94 21 ff e0                  	stwu 1, -32(1)
     20c: 3c 60 00 00                  	lis 3, 0
     210: 93 81 00 10                  	stw 28, 16(1)
     214: 7c 9c 23 78                  	mr 28, 4
     218: 38 63 00 00                  	addi 3, 3, 0
     21c: 38 80 00 53                  	li 4, 83
     220: 93 a1 00 14                  	stw 29, 20(1)
     224: 7c bd 2b 78                  	mr 29, 5
     228: 38 a0 00 00                  	li 5, 0
     22c: 93 c1 00 18                  	stw 30, 24(1)
     230: 7c de 33 78                  	mr 30, 6
     234: 48 00 00 01                  	bl .+0
     238: 3c 60 00 00                  	lis 3, 0
     23c: 3c 80 cc cc                  	lis 4, -13108
     240: 80 63 00 00                  	lwz 3, 0(3)
     244: 60 84 d0 00                  	ori 4, 4, 53248
     248: 7c 63 e0 50                  	subf 3, 3, 28
     24c: 54 63 e8 fe                  	srwi 3, 3, 3
     250: 7c 63 21 d6                  	mullw 3, 3, 4
     254: 57 a4 05 3e                  	clrlwi	4, 29, 20
     258: 50 64 00 26                  	rlwimi 4, 3, 0, 0, 19
     25c: 3c 64 c0 00                  	addis 3, 4, -16384
     260: 7c 83 f2 14                  	add 4, 3, 30
     264: 48 00 00 01                  	bl .+0
     268: 83 c1 00 18                  	lwz 30, 24(1)
     26c: 83 a1 00 14                  	lwz 29, 20(1)
     270: 83 81 00 10                  	lwz 28, 16(1)
     274: 80 01 00 24                  	lwz 0, 36(1)
     278: 38 21 00 20                  	addi 1, 1, 32
     27c: 7c 08 03 a6                  	mtlr 0
     280: 4e 80 00 20                  	blr

00000284 update_mmu_cache:
     284: 4e 80 00 20                  	blr

00000288 devmem_is_allowed:
     288: 7c 08 02 a6                  	mflr 0
     28c: 90 01 00 04                  	stw 0, 4(1)
     290: 94 21 ff f0                  	stwu 1, -16(1)
     294: 93 c1 00 08                  	stw 30, 8(1)
     298: 7c 7e 1b 78                  	mr 30, 3
     29c: 54 63 65 3e                  	srwi 3, 3, 20
     2a0: 57 c4 60 26                  	slwi 4, 30, 12
     2a4: 48 00 00 01                  	bl .+0
     2a8: 70 63 00 01                  	andi. 3, 3, 1
     2ac: 38 60 00 00                  	li 3, 0
     2b0: 41 81 00 14                  	bt	1, .+20
     2b4: 7f c3 f3 78                  	mr 3, 30
     2b8: 48 00 00 01                  	bl .+0
     2bc: 7c 63 00 34                  	cntlzw	3, 3
     2c0: 54 63 df fe                  	rlwinm 3, 3, 27, 31, 31
     2c4: 83 c1 00 08                  	lwz 30, 8(1)
     2c8: 80 01 00 14                  	lwz 0, 20(1)
     2cc: 38 21 00 10                  	addi 1, 1, 16
     2d0: 7c 08 03 a6                  	mtlr 0
     2d4: 4e 80 00 20                  	blr

Disassembly of section .init.text:

00000000 mem_topology_setup:
       0: 7c 08 02 a6                  	mflr 0
       4: 90 01 00 04                  	stw 0, 4(1)
       8: 94 21 ff f0                  	stwu 1, -16(1)
       c: 48 00 00 01                  	bl .+0
      10: 54 84 a0 3e                  	rotlwi	4, 4, 20
      14: 3c a0 00 00                  	lis 5, 0
      18: 50 64 a0 16                  	rlwimi 4, 3, 20, 0, 11
      1c: 3c 60 00 00                  	lis 3, 0
      20: 90 85 00 00                  	stw 4, 0(5)
      24: 38 a0 ff ff                  	li 5, -1
      28: 90 83 00 00                  	stw 4, 0(3)
      2c: 3c 60 00 00                  	lis 3, 0
      30: 38 80 00 00                  	li 4, 0
      34: 38 c0 ff ff                  	li 6, -1
      38: 39 00 00 00                  	li 8, 0
      3c: 90 83 00 00                  	stw 4, 0(3)
      40: 3c 60 00 00                  	lis 3, 0
      44: 38 80 00 00                  	li 4, 0
      48: 38 63 00 00                  	addi 3, 3, 0
      4c: 38 e3 00 10                  	addi 7, 3, 16
      50: 38 60 00 00                  	li 3, 0
      54: 48 00 00 01                  	bl .+0
      58: 80 01 00 14                  	lwz 0, 20(1)
      5c: 38 21 00 10                  	addi 1, 1, 16
      60: 7c 08 03 a6                  	mtlr 0
      64: 4e 80 00 20                  	blr

00000068 initmem_init:
      68: 7c 08 02 a6                  	mflr 0
      6c: 90 01 00 04                  	stw 0, 4(1)
      70: 94 21 ff f0                  	stwu 1, -16(1)
      74: 38 60 00 00                  	li 3, 0
      78: 48 00 00 01                  	bl .+0
      7c: 80 01 00 14                  	lwz 0, 20(1)
      80: 38 21 00 10                  	addi 1, 1, 16
      84: 7c 08 03 a6                  	mtlr 0
      88: 4e 80 00 20                  	blr

0000008c paging_init:
      8c: 7c 08 02 a6                  	mflr 0
      90: 90 01 00 04                  	stw 0, 4(1)
      94: 94 21 ff d0                  	stwu 1, -48(1)
      98: 93 21 00 14                  	stw 25, 20(1)
      9c: 93 41 00 18                  	stw 26, 24(1)
      a0: 93 61 00 1c                  	stw 27, 28(1)
      a4: 93 81 00 20                  	stw 28, 32(1)
      a8: 93 a1 00 24                  	stw 29, 36(1)
      ac: 93 c1 00 28                  	stw 30, 40(1)
      b0: 48 00 00 01                  	bl .+0
      b4: 7c 7e 1b 78                  	mr 30, 3
      b8: 7c 9d 23 78                  	mr 29, 4
      bc: 3f 40 ff fe                  	lis 26, -2
      c0: 48 00 00 01                  	bl .+0
      c4: 7c 7c 1b 78                  	mr 28, 3
      c8: 7c 9b 23 78                  	mr 27, 4
      cc: 3b 20 00 00                  	li 25, 0
      d0: 38 e1 00 08                  	addi 7, 1, 8
      d4: 7f 43 d3 78                  	mr 3, 26
      d8: 93 21 00 10                  	stw 25, 16(1)
      dc: 38 a0 00 00                  	li 5, 0
      e0: 38 c0 00 00                  	li 6, 0
      e4: 93 21 00 08                  	stw 25, 8(1)
      e8: 48 00 00 01                  	bl .+0
      ec: 3b 5a 10 00                  	addi 26, 26, 4096
      f0: 2c 1a f0 00                  	cmpwi	26, -4096
      f4: 40 82 ff dc                  	bf	2, .+65500
      f8: 3c 60 00 00                  	lis 3, 0
      fc: 4c c6 31 82                  	crclr	6
     100: 7f 85 e3 78                  	mr 5, 28
     104: 38 63 00 00                  	addi 3, 3, 0
     108: 7f 66 db 78                  	mr 6, 27
     10c: 7f c7 f3 78                  	mr 7, 30
     110: 7f a8 eb 78                  	mr 8, 29
     114: 48 00 00 01                  	bl .+0
     118: 7c 7d d8 10                  	subfc 3, 29, 27
     11c: 4c c6 31 82                  	crclr	6
     120: 7c be e1 10                  	subfe 5, 30, 28
     124: 54 64 60 3e                  	rotlwi	4, 3, 12
     128: 3c 60 00 00                  	lis 3, 0
     12c: 50 a4 60 26                  	rlwimi 4, 5, 12, 0, 19
     130: 38 63 00 00                  	addi 3, 3, 0
     134: 48 00 00 01                  	bl .+0
     138: 3c 60 00 00                  	lis 3, 0
     13c: 80 83 00 00                  	lwz 4, 0(3)
     140: 3c 60 00 00                  	lis 3, 0
     144: 94 83 00 00                  	stwu 4, 0(3)
     148: 48 00 00 01                  	bl .+0
     14c: 83 c1 00 28                  	lwz 30, 40(1)
     150: 83 a1 00 24                  	lwz 29, 36(1)
     154: 83 81 00 20                  	lwz 28, 32(1)
     158: 83 61 00 1c                  	lwz 27, 28(1)
     15c: 83 41 00 18                  	lwz 26, 24(1)
     160: 83 21 00 14                  	lwz 25, 20(1)
     164: 80 01 00 34                  	lwz 0, 52(1)
     168: 38 21 00 30                  	addi 1, 1, 48
     16c: 7c 08 03 a6                  	mtlr 0
     170: 4e 80 00 20                  	blr

00000174 mem_init:
     174: 7c 08 02 a6                  	mflr 0
     178: 90 01 00 04                  	stw 0, 4(1)
     17c: 94 21 ff e0                  	stwu 1, -32(1)
     180: 3c 60 00 00                  	lis 3, 0
     184: 3c 80 00 00                  	lis 4, 0
     188: 93 81 00 10                  	stw 28, 16(1)
     18c: 3f 80 00 00                  	lis 28, 0
     190: 80 63 00 00                  	lwz 3, 0(3)
     194: 80 84 00 00                  	lwz 4, 0(4)
     198: 54 63 60 26                  	slwi 3, 3, 12
     19c: 3c 63 c0 00                  	addis 3, 3, -16384
     1a0: 90 7c 00 00                  	stw 3, 0(28)
     1a4: 3c 60 00 00                  	lis 3, 0
     1a8: 93 a1 00 14                  	stw 29, 20(1)
     1ac: 93 c1 00 18                  	stw 30, 24(1)
     1b0: 90 83 00 00                  	stw 4, 0(3)
     1b4: 48 00 00 01                  	bl .+0
     1b8: 38 60 00 00                  	li 3, 0
     1bc: 48 00 00 01                  	bl .+0
     1c0: 3c 60 00 00                  	lis 3, 0
     1c4: 4c c6 31 82                  	crclr	6
     1c8: 38 63 00 00                  	addi 3, 3, 0
     1cc: 48 00 00 01                  	bl .+0
     1d0: 3c 80 ff fd                  	lis 4, -3
     1d4: 3c 60 00 00                  	lis 3, 0
     1d8: 4c c6 31 82                  	crclr	6
     1dc: 60 9e f0 00                  	ori 30, 4, 61440
     1e0: 38 63 00 00                  	addi 3, 3, 0
     1e4: 7f c4 f3 78                  	mr 4, 30
     1e8: 38 a0 f0 00                  	li 5, -4096
     1ec: 48 00 00 01                  	bl .+0
     1f0: 3c 80 ff dd                  	lis 4, -35
     1f4: 3c 60 00 00                  	lis 3, 0
     1f8: 4c c6 31 82                  	crclr	6
     1fc: 60 9d f0 00                  	ori 29, 4, 61440
     200: 38 63 00 00                  	addi 3, 3, 0
     204: 7f a4 eb 78                  	mr 4, 29
     208: 7f c5 f3 78                  	mr 5, 30
     20c: 48 00 00 01                  	bl .+0
     210: 3f c0 00 00                  	lis 30, 0
     214: 3c 60 00 00                  	lis 3, 0
     218: 4c c6 31 82                  	crclr	6
     21c: 80 9e 00 00                  	lwz 4, 0(30)
     220: 38 63 00 00                  	addi 3, 3, 0
     224: 7f a5 eb 78                  	mr 5, 29
     228: 48 00 00 01                  	bl .+0
     22c: 80 7c 00 00                  	lwz 3, 0(28)
     230: 3c 80 01 00                  	lis 4, 256
     234: 4c c6 31 82                  	crclr	6
     238: 38 63 ff ff                  	addi 3, 3, -1
     23c: 80 be 00 00                  	lwz 5, 0(30)
     240: 3c 63 10 00                  	addis 3, 3, 4096
     244: 54 63 00 06                  	rlwinm 3, 3, 0, 0, 3
     248: 50 64 02 0c                  	rlwimi 4, 3, 0, 8, 6
     24c: 3c 60 00 00                  	lis 3, 0
     250: 38 63 00 00                  	addi 3, 3, 0
     254: 48 00 00 01                  	bl .+0
     258: 83 c1 00 18                  	lwz 30, 24(1)
     25c: 83 a1 00 14                  	lwz 29, 20(1)
     260: 83 81 00 10                  	lwz 28, 16(1)
     264: 80 01 00 24                  	lwz 0, 36(1)
     268: 38 21 00 20                  	addi 1, 1, 32
     26c: 7c 08 03 a6                  	mtlr 0
     270: 4e 80 00 20                  	blr

00000274 add_system_ram_resources:
     274: 7c 08 02 a6                  	mflr 0
     278: 90 01 00 04                  	stw 0, 4(1)
     27c: 94 21 ff c0                  	stwu 1, -64(1)
     280: 3c 60 00 00                  	lis 3, 0
     284: 93 a1 00 34                  	stw 29, 52(1)
     288: 3b a3 00 00                  	addi 29, 3, 0
     28c: 80 7d 00 10                  	lwz 3, 16(29)
     290: 93 81 00 30                  	stw 28, 48(1)
     294: 1c 63 00 18                  	mulli 3, 3, 24
     298: 83 9d 00 20                  	lwz 28, 32(29)
     29c: 7c 7c 1a 14                  	add 3, 28, 3
     2a0: 92 a1 00 14                  	stw 21, 20(1)
     2a4: 7c 1c 18 40                  	cmplw	28, 3
     2a8: 92 c1 00 18                  	stw 22, 24(1)
     2ac: 92 e1 00 1c                  	stw 23, 28(1)
     2b0: 93 01 00 20                  	stw 24, 32(1)
     2b4: 93 21 00 24                  	stw 25, 36(1)
     2b8: 93 41 00 28                  	stw 26, 40(1)
     2bc: 93 61 00 2c                  	stw 27, 44(1)
     2c0: 93 c1 00 38                  	stw 30, 56(1)
     2c4: 40 80 00 ac                  	bf	0, .+172
     2c8: 3c 60 00 00                  	lis 3, 0
     2cc: 3c 80 00 00                  	lis 4, 0
     2d0: 3c a0 81 00                  	lis 5, -32512
     2d4: 3c c0 00 00                  	lis 6, 0
     2d8: 3b 7c ff e8                  	addi 27, 28, -24
     2dc: 3b 40 00 00                  	li 26, 0
     2e0: 3b 23 00 00                  	addi 25, 3, 0
     2e4: 3b 04 00 00                  	addi 24, 4, 0
     2e8: 60 b7 02 00                  	ori 23, 5, 512
     2ec: 3b c6 00 00                  	addi 30, 6, 0
     2f0: 80 79 00 18                  	lwz 3, 24(25)
     2f4: 38 80 0d c0                  	li 4, 3520
     2f8: 38 a0 00 28                  	li 5, 40
     2fc: 82 db 00 1c                  	lwz 22, 28(27)
     300: 82 bb 00 24                  	lwz 21, 36(27)
     304: 48 00 00 01                  	bl .+0
     308: 7c 64 1b 78                  	mr 4, 3
     30c: 7c 63 00 34                  	cntlzw	3, 3
     310: 28 04 00 00                  	cmplwi	4, 0
     314: 54 63 df fe                  	rlwinm 3, 3, 27, 31, 31
     318: 0f 03 00 00                  	twnei	3, 0
     31c: 41 82 00 34                  	bt	2, .+52
     320: 7c 76 aa 14                  	add 3, 22, 21
     324: 93 04 00 10                  	stw 24, 16(4)
     328: 38 63 ff ff                  	addi 3, 3, -1
     32c: 90 64 00 0c                  	stw 3, 12(4)
     330: 7f c3 f3 78                  	mr 3, 30
     334: 93 44 00 00                  	stw 26, 0(4)
     338: 92 c4 00 04                  	stw 22, 4(4)
     33c: 93 44 00 08                  	stw 26, 8(4)
     340: 92 e4 00 14                  	stw 23, 20(4)
     344: 48 00 00 01                  	bl .+0
     348: 54 63 0f fe                  	srwi 3, 3, 31
     34c: 0f 03 00 00                  	twnei	3, 0
     350: 80 7d 00 10                  	lwz 3, 16(29)
     354: 3b 9c 00 18                  	addi 28, 28, 24
     358: 3b 7b 00 18                  	addi 27, 27, 24
     35c: 80 9d 00 20                  	lwz 4, 32(29)
     360: 1c 63 00 18                  	mulli 3, 3, 24
     364: 7c 64 1a 14                  	add 3, 4, 3
     368: 7c 1c 18 40                  	cmplw	28, 3
     36c: 41 80 ff 84                  	bt	0, .+65412
     370: 38 60 00 00                  	li 3, 0
     374: 83 c1 00 38                  	lwz 30, 56(1)
     378: 83 a1 00 34                  	lwz 29, 52(1)
     37c: 83 81 00 30                  	lwz 28, 48(1)
     380: 83 61 00 2c                  	lwz 27, 44(1)
     384: 83 41 00 28                  	lwz 26, 40(1)
     388: 83 21 00 24                  	lwz 25, 36(1)
     38c: 83 01 00 20                  	lwz 24, 32(1)
     390: 82 e1 00 1c                  	lwz 23, 28(1)
     394: 82 c1 00 18                  	lwz 22, 24(1)
     398: 82 a1 00 14                  	lwz 21, 20(1)
     39c: 80 01 00 44                  	lwz 0, 68(1)
     3a0: 38 21 00 40                  	addi 1, 1, 64
     3a4: 7c 08 03 a6                  	mtlr 0
     3a8: 4e 80 00 20                  	blr

[-- Attachment #4: mem-broken.txt --]
[-- Type: text/plain, Size: 22322 bytes --]


mem-broken.o:	file format ELF32-ppc


Disassembly of section .text:

00000000 phys_mem_access_prot:
       0: 7c 08 02 a6                  	mflr 0
       4: 90 01 00 04                  	stw 0, 4(1)
       8: 94 21 ff e0                  	stwu 1, -32(1)
       c: 93 a1 00 14                  	stw 29, 20(1)
      10: 7c fd 3b 78                  	mr 29, 7
      14: 3c e0 00 00                  	lis 7, 0
      18: 38 e7 00 00                  	addi 7, 7, 0
      1c: 81 07 00 a0                  	lwz 8, 160(7)
      20: 93 c1 00 18                  	stw 30, 24(1)
      24: 28 08 00 00                  	cmplwi	8, 0
      28: 7c 7e 1b 78                  	mr 30, 3
      2c: 41 82 00 20                  	bt	2, .+32
      30: 80 7d 00 00                  	lwz 3, 0(29)
      34: 38 e1 00 08                  	addi 7, 1, 8
      38: 7d 09 03 a6                  	mtctr 8
      3c: 90 61 00 08                  	stw 3, 8(1)
      40: 7f c3 f3 78                  	mr 3, 30
      44: 4e 80 04 21                  	bctrl
      48: 48 00 00 28                  	b .+40
      4c: 7c a3 2b 78                  	mr 3, 5
      50: 48 00 00 01                  	bl .+0
      54: 28 03 00 00                  	cmplwi	3, 0
      58: 80 7d 00 00                  	lwz 3, 0(29)
      5c: 40 82 00 10                  	bf	2, .+16
      60: 54 63 06 26                  	rlwinm 3, 3, 0, 24, 19
      64: 60 63 05 00                  	ori 3, 3, 1280
      68: 90 7d 00 00                  	stw 3, 0(29)
      6c: 90 7e 00 00                  	stw 3, 0(30)
      70: 83 c1 00 18                  	lwz 30, 24(1)
      74: 83 a1 00 14                  	lwz 29, 20(1)
      78: 80 01 00 24                  	lwz 0, 36(1)
      7c: 38 21 00 20                  	addi 1, 1, 32
      80: 7c 08 03 a6                  	mtlr 0
      84: 4e 80 00 20                  	blr

00000088 free_initmem:
      88: 7c 08 02 a6                  	mflr 0
      8c: 90 01 00 04                  	stw 0, 4(1)
      90: 94 21 ff f0                  	stwu 1, -16(1)
      94: 3c 60 00 00                  	lis 3, 0
      98: 3c 80 00 00                  	lis 4, 0
      9c: 38 63 00 00                  	addi 3, 3, 0
      a0: 38 84 00 00                  	addi 4, 4, 0
      a4: 90 83 00 60                  	stw 4, 96(3)
      a8: 48 00 00 01                  	bl .+0
      ac: 3c 60 00 00                  	lis 3, 0
      b0: 38 80 00 01                  	li 4, 1
      b4: 98 83 00 00                  	stb 4, 0(3)
      b8: 3c 60 00 00                  	lis 3, 0
      bc: 3c 80 00 00                  	lis 4, 0
      c0: 3c a0 00 00                  	lis 5, 0
      c4: 38 63 00 00                  	addi 3, 3, 0
      c8: 38 84 00 00                  	addi 4, 4, 0
      cc: 38 c5 00 00                  	addi 6, 5, 0
      d0: 38 a0 00 cc                  	li 5, 204
      d4: 48 00 00 01                  	bl .+0
      d8: 80 01 00 14                  	lwz 0, 20(1)
      dc: 38 21 00 10                  	addi 1, 1, 16
      e0: 7c 08 03 a6                  	mtlr 0
      e4: 4e 80 00 20                  	blr

000000e8 flush_dcache_page:
      e8: 80 83 00 00                  	lwz 4, 0(3)
      ec: 70 84 08 00                  	andi. 4, 4, 2048
      f0: 4d 82 00 20                  	bclr	12, 2
      f4: 38 80 08 00                  	li 4, 2048
      f8: 7c a0 18 28                  	lwarx 5, 0, 3
      fc: 7c a5 20 78                  	andc 5, 5, 4
     100: 7c a0 19 2d                  	stwcx. 5, 0, 3
     104: 40 a2 ff f4                  	bc 5, 2, .+65524
     108: 4e 80 00 20                  	blr

0000010c flush_dcache_icache_page:
     10c: 7c 08 02 a6                  	mflr 0
     110: 90 01 00 04                  	stw 0, 4(1)
     114: 94 21 ff f0                  	stwu 1, -16(1)
     118: 80 82 00 00                  	lwz 4, 0(2)
     11c: 38 84 00 01                  	addi 4, 4, 1
     120: 90 82 00 00                  	stw 4, 0(2)
     124: 80 82 0a e4                  	lwz 4, 2788(2)
     128: 38 84 00 01                  	addi 4, 4, 1
     12c: 90 82 0a e4                  	stw 4, 2788(2)
     130: 3c 80 00 00                  	lis 4, 0
     134: 80 84 00 00                  	lwz 4, 0(4)
     138: 7c 64 18 50                  	subf 3, 4, 3
     13c: 3c 80 cc cc                  	lis 4, -13108
     140: 54 63 e8 fe                  	srwi 3, 3, 3
     144: 60 84 d0 00                  	ori 4, 4, 53248
     148: 7c 63 21 d6                  	mullw 3, 3, 4
     14c: 3c 63 c0 00                  	addis 3, 3, -16384
     150: 48 00 00 01                  	bl .+0
     154: 80 62 0a e4                  	lwz 3, 2788(2)
     158: 38 63 ff ff                  	addi 3, 3, -1
     15c: 90 62 0a e4                  	stw 3, 2788(2)
     160: 80 62 00 00                  	lwz 3, 0(2)
     164: 38 63 ff ff                  	addi 3, 3, -1
     168: 90 62 00 00                  	stw 3, 0(2)
     16c: 80 01 00 14                  	lwz 0, 20(1)
     170: 38 21 00 10                  	addi 1, 1, 16
     174: 7c 08 03 a6                  	mtlr 0
     178: 4e 80 00 20                  	blr

0000017c clear_user_page:
     17c: 94 21 ff f0                  	stwu 1, -16(1)
     180: 38 80 00 80                  	li 4, 128
     184: 38 63 ff e0                  	addi 3, 3, -32
     188: 7c 89 03 a6                  	mtctr 4
     18c: 38 81 00 0f                  	addi 4, 1, 15
     190: 8c c3 00 20                  	lbzu 6, 32(3)
     194: 98 c1 00 0f                  	stb 6, 15(1)
     198: 7c 00 27 ec                  	dcbz 0, 4
     19c: 42 00 ff f4                  	bdnz .+65524
     1a0: 80 65 00 00                  	lwz 3, 0(5)
     1a4: 70 63 08 00                  	andi. 3, 3, 2048
     1a8: 41 82 00 18                  	bt	2, .+24
     1ac: 38 60 08 00                  	li 3, 2048
     1b0: 7c 80 28 28                  	lwarx 4, 0, 5
     1b4: 7c 84 18 78                  	andc 4, 4, 3
     1b8: 7c 80 29 2d                  	stwcx. 4, 0, 5
     1bc: 40 a2 ff f4                  	bc 5, 2, .+65524
     1c0: 38 21 00 10                  	addi 1, 1, 16
     1c4: 4e 80 00 20                  	blr

000001c8 copy_user_page:
     1c8: 7c 08 02 a6                  	mflr 0
     1cc: 90 01 00 04                  	stw 0, 4(1)
     1d0: 94 21 ff f0                  	stwu 1, -16(1)
     1d4: 93 c1 00 08                  	stw 30, 8(1)
     1d8: 7c de 33 78                  	mr 30, 6
     1dc: 48 00 00 01                  	bl .+0
     1e0: 80 7e 00 00                  	lwz 3, 0(30)
     1e4: 70 63 08 00                  	andi. 3, 3, 2048
     1e8: 41 82 00 18                  	bt	2, .+24
     1ec: 38 60 08 00                  	li 3, 2048
     1f0: 7c 80 f0 28                  	lwarx 4, 0, 30
     1f4: 7c 84 18 78                  	andc 4, 4, 3
     1f8: 7c 80 f1 2d                  	stwcx. 4, 0, 30
     1fc: 40 a2 ff f4                  	bc 5, 2, .+65524
     200: 83 c1 00 08                  	lwz 30, 8(1)
     204: 80 01 00 14                  	lwz 0, 20(1)
     208: 38 21 00 10                  	addi 1, 1, 16
     20c: 7c 08 03 a6                  	mtlr 0
     210: 4e 80 00 20                  	blr

00000214 flush_icache_user_range:
     214: 7c 08 02 a6                  	mflr 0
     218: 90 01 00 04                  	stw 0, 4(1)
     21c: 94 21 ff e0                  	stwu 1, -32(1)
     220: 3c 60 00 00                  	lis 3, 0
     224: 93 81 00 10                  	stw 28, 16(1)
     228: 7c 9c 23 78                  	mr 28, 4
     22c: 38 63 00 00                  	addi 3, 3, 0
     230: 38 80 00 53                  	li 4, 83
     234: 93 a1 00 14                  	stw 29, 20(1)
     238: 7c bd 2b 78                  	mr 29, 5
     23c: 38 a0 00 00                  	li 5, 0
     240: 93 c1 00 18                  	stw 30, 24(1)
     244: 7c de 33 78                  	mr 30, 6
     248: 48 00 00 01                  	bl .+0
     24c: 3c 60 00 00                  	lis 3, 0
     250: 3c 80 cc cc                  	lis 4, -13108
     254: 80 63 00 00                  	lwz 3, 0(3)
     258: 60 84 d0 00                  	ori 4, 4, 53248
     25c: 7c 63 e0 50                  	subf 3, 3, 28
     260: 54 63 e8 fe                  	srwi 3, 3, 3
     264: 7c 63 21 d6                  	mullw 3, 3, 4
     268: 57 a4 05 3e                  	clrlwi	4, 29, 20
     26c: 50 64 00 26                  	rlwimi 4, 3, 0, 0, 19
     270: 3c 64 c0 00                  	addis 3, 4, -16384
     274: 7c 83 f2 14                  	add 4, 3, 30
     278: 48 00 00 01                  	bl .+0
     27c: 83 c1 00 18                  	lwz 30, 24(1)
     280: 83 a1 00 14                  	lwz 29, 20(1)
     284: 83 81 00 10                  	lwz 28, 16(1)
     288: 80 01 00 24                  	lwz 0, 36(1)
     28c: 38 21 00 20                  	addi 1, 1, 32
     290: 7c 08 03 a6                  	mtlr 0
     294: 4e 80 00 20                  	blr

00000298 update_mmu_cache:
     298: 4e 80 00 20                  	blr

0000029c devmem_is_allowed:
     29c: 7c 08 02 a6                  	mflr 0
     2a0: 90 01 00 04                  	stw 0, 4(1)
     2a4: 94 21 ff f0                  	stwu 1, -16(1)
     2a8: 93 c1 00 08                  	stw 30, 8(1)
     2ac: 7c 7e 1b 78                  	mr 30, 3
     2b0: 54 63 65 3e                  	srwi 3, 3, 20
     2b4: 57 c4 60 26                  	slwi 4, 30, 12
     2b8: 48 00 00 01                  	bl .+0
     2bc: 70 63 00 01                  	andi. 3, 3, 1
     2c0: 38 60 00 00                  	li 3, 0
     2c4: 41 81 00 14                  	bt	1, .+20
     2c8: 7f c3 f3 78                  	mr 3, 30
     2cc: 48 00 00 01                  	bl .+0
     2d0: 7c 63 00 34                  	cntlzw	3, 3
     2d4: 54 63 df fe                  	rlwinm 3, 3, 27, 31, 31
     2d8: 83 c1 00 08                  	lwz 30, 8(1)
     2dc: 80 01 00 14                  	lwz 0, 20(1)
     2e0: 38 21 00 10                  	addi 1, 1, 16
     2e4: 7c 08 03 a6                  	mtlr 0
     2e8: 4e 80 00 20                  	blr

Disassembly of section .init.text:

00000000 mem_topology_setup:
       0: 7c 08 02 a6                  	mflr 0
       4: 90 01 00 04                  	stw 0, 4(1)
       8: 94 21 ff f0                  	stwu 1, -16(1)
       c: 48 00 00 01                  	bl .+0
      10: 54 84 a0 3e                  	rotlwi	4, 4, 20
      14: 3c a0 00 00                  	lis 5, 0
      18: 50 64 a0 16                  	rlwimi 4, 3, 20, 0, 11
      1c: 3c 60 00 00                  	lis 3, 0
      20: 90 85 00 00                  	stw 4, 0(5)
      24: 38 a0 ff ff                  	li 5, -1
      28: 90 83 00 00                  	stw 4, 0(3)
      2c: 3c 60 00 00                  	lis 3, 0
      30: 38 80 00 00                  	li 4, 0
      34: 38 c0 ff ff                  	li 6, -1
      38: 39 00 00 00                  	li 8, 0
      3c: 90 83 00 00                  	stw 4, 0(3)
      40: 3c 60 00 00                  	lis 3, 0
      44: 38 80 00 00                  	li 4, 0
      48: 38 63 00 00                  	addi 3, 3, 0
      4c: 38 e3 00 10                  	addi 7, 3, 16
      50: 38 60 00 00                  	li 3, 0
      54: 48 00 00 01                  	bl .+0
      58: 80 01 00 14                  	lwz 0, 20(1)
      5c: 38 21 00 10                  	addi 1, 1, 16
      60: 7c 08 03 a6                  	mtlr 0
      64: 4e 80 00 20                  	blr

00000068 initmem_init:
      68: 7c 08 02 a6                  	mflr 0
      6c: 90 01 00 04                  	stw 0, 4(1)
      70: 94 21 ff f0                  	stwu 1, -16(1)
      74: 38 60 00 00                  	li 3, 0
      78: 48 00 00 01                  	bl .+0
      7c: 80 01 00 14                  	lwz 0, 20(1)
      80: 38 21 00 10                  	addi 1, 1, 16
      84: 7c 08 03 a6                  	mtlr 0
      88: 4e 80 00 20                  	blr

0000008c paging_init:
      8c: 7c 08 02 a6                  	mflr 0
      90: 90 01 00 04                  	stw 0, 4(1)
      94: 94 21 ff d0                  	stwu 1, -48(1)
      98: 93 21 00 14                  	stw 25, 20(1)
      9c: 93 41 00 18                  	stw 26, 24(1)
      a0: 93 61 00 1c                  	stw 27, 28(1)
      a4: 93 81 00 20                  	stw 28, 32(1)
      a8: 93 a1 00 24                  	stw 29, 36(1)
      ac: 93 c1 00 28                  	stw 30, 40(1)
      b0: 48 00 00 01                  	bl .+0
      b4: 7c 7e 1b 78                  	mr 30, 3
      b8: 7c 9d 23 78                  	mr 29, 4
      bc: 3f 40 ff fe                  	lis 26, -2
      c0: 48 00 00 01                  	bl .+0
      c4: 7c 7c 1b 78                  	mr 28, 3
      c8: 7c 9b 23 78                  	mr 27, 4
      cc: 3b 20 00 00                  	li 25, 0
      d0: 38 e1 00 08                  	addi 7, 1, 8
      d4: 7f 43 d3 78                  	mr 3, 26
      d8: 93 21 00 10                  	stw 25, 16(1)
      dc: 38 a0 00 00                  	li 5, 0
      e0: 38 c0 00 00                  	li 6, 0
      e4: 93 21 00 08                  	stw 25, 8(1)
      e8: 48 00 00 01                  	bl .+0
      ec: 3b 5a 10 00                  	addi 26, 26, 4096
      f0: 2c 1a f0 00                  	cmpwi	26, -4096
      f4: 40 82 ff dc                  	bf	2, .+65500
      f8: 3c 60 00 00                  	lis 3, 0
      fc: 4c c6 31 82                  	crclr	6
     100: 7f 85 e3 78                  	mr 5, 28
     104: 38 63 00 00                  	addi 3, 3, 0
     108: 7f 66 db 78                  	mr 6, 27
     10c: 7f c7 f3 78                  	mr 7, 30
     110: 7f a8 eb 78                  	mr 8, 29
     114: 48 00 00 01                  	bl .+0
     118: 7c 7d d8 10                  	subfc 3, 29, 27
     11c: 4c c6 31 82                  	crclr	6
     120: 7c be e1 10                  	subfe 5, 30, 28
     124: 54 64 60 3e                  	rotlwi	4, 3, 12
     128: 3c 60 00 00                  	lis 3, 0
     12c: 50 a4 60 26                  	rlwimi 4, 5, 12, 0, 19
     130: 38 63 00 00                  	addi 3, 3, 0
     134: 48 00 00 01                  	bl .+0
     138: 3c 60 00 00                  	lis 3, 0
     13c: 80 83 00 00                  	lwz 4, 0(3)
     140: 3c 60 00 00                  	lis 3, 0
     144: 94 83 00 00                  	stwu 4, 0(3)
     148: 48 00 00 01                  	bl .+0
     14c: 83 c1 00 28                  	lwz 30, 40(1)
     150: 83 a1 00 24                  	lwz 29, 36(1)
     154: 83 81 00 20                  	lwz 28, 32(1)
     158: 83 61 00 1c                  	lwz 27, 28(1)
     15c: 83 41 00 18                  	lwz 26, 24(1)
     160: 83 21 00 14                  	lwz 25, 20(1)
     164: 80 01 00 34                  	lwz 0, 52(1)
     168: 38 21 00 30                  	addi 1, 1, 48
     16c: 7c 08 03 a6                  	mtlr 0
     170: 4e 80 00 20                  	blr

00000174 mem_init:
     174: 7c 08 02 a6                  	mflr 0
     178: 90 01 00 04                  	stw 0, 4(1)
     17c: 94 21 ff e0                  	stwu 1, -32(1)
     180: 3c 60 00 00                  	lis 3, 0
     184: 3c 80 00 00                  	lis 4, 0
     188: 93 81 00 10                  	stw 28, 16(1)
     18c: 3f 80 00 00                  	lis 28, 0
     190: 80 63 00 00                  	lwz 3, 0(3)
     194: 80 84 00 00                  	lwz 4, 0(4)
     198: 54 63 60 26                  	slwi 3, 3, 12
     19c: 3c 63 c0 00                  	addis 3, 3, -16384
     1a0: 90 7c 00 00                  	stw 3, 0(28)
     1a4: 3c 60 00 00                  	lis 3, 0
     1a8: 93 a1 00 14                  	stw 29, 20(1)
     1ac: 93 c1 00 18                  	stw 30, 24(1)
     1b0: 90 83 00 00                  	stw 4, 0(3)
     1b4: 48 00 00 01                  	bl .+0
     1b8: 38 60 00 00                  	li 3, 0
     1bc: 48 00 00 01                  	bl .+0
     1c0: 3c 60 00 00                  	lis 3, 0
     1c4: 4c c6 31 82                  	crclr	6
     1c8: 38 63 00 00                  	addi 3, 3, 0
     1cc: 48 00 00 01                  	bl .+0
     1d0: 3c 80 ff fd                  	lis 4, -3
     1d4: 3c 60 00 00                  	lis 3, 0
     1d8: 4c c6 31 82                  	crclr	6
     1dc: 60 9e f0 00                  	ori 30, 4, 61440
     1e0: 38 63 00 00                  	addi 3, 3, 0
     1e4: 7f c4 f3 78                  	mr 4, 30
     1e8: 38 a0 f0 00                  	li 5, -4096
     1ec: 48 00 00 01                  	bl .+0
     1f0: 3c 80 ff dd                  	lis 4, -35
     1f4: 3c 60 00 00                  	lis 3, 0
     1f8: 4c c6 31 82                  	crclr	6
     1fc: 60 9d f0 00                  	ori 29, 4, 61440
     200: 38 63 00 00                  	addi 3, 3, 0
     204: 7f a4 eb 78                  	mr 4, 29
     208: 7f c5 f3 78                  	mr 5, 30
     20c: 48 00 00 01                  	bl .+0
     210: 3f c0 00 00                  	lis 30, 0
     214: 3c 60 00 00                  	lis 3, 0
     218: 4c c6 31 82                  	crclr	6
     21c: 80 9e 00 00                  	lwz 4, 0(30)
     220: 38 63 00 00                  	addi 3, 3, 0
     224: 7f a5 eb 78                  	mr 5, 29
     228: 48 00 00 01                  	bl .+0
     22c: 80 7c 00 00                  	lwz 3, 0(28)
     230: 3c 80 01 00                  	lis 4, 256
     234: 4c c6 31 82                  	crclr	6
     238: 38 63 ff ff                  	addi 3, 3, -1
     23c: 80 be 00 00                  	lwz 5, 0(30)
     240: 3c 63 10 00                  	addis 3, 3, 4096
     244: 54 63 00 06                  	rlwinm 3, 3, 0, 0, 3
     248: 50 64 02 0c                  	rlwimi 4, 3, 0, 8, 6
     24c: 3c 60 00 00                  	lis 3, 0
     250: 38 63 00 00                  	addi 3, 3, 0
     254: 48 00 00 01                  	bl .+0
     258: 83 c1 00 18                  	lwz 30, 24(1)
     25c: 83 a1 00 14                  	lwz 29, 20(1)
     260: 83 81 00 10                  	lwz 28, 16(1)
     264: 80 01 00 24                  	lwz 0, 36(1)
     268: 38 21 00 20                  	addi 1, 1, 32
     26c: 7c 08 03 a6                  	mtlr 0
     270: 4e 80 00 20                  	blr

00000274 add_system_ram_resources:
     274: 7c 08 02 a6                  	mflr 0
     278: 90 01 00 04                  	stw 0, 4(1)
     27c: 94 21 ff c0                  	stwu 1, -64(1)
     280: 3c 60 00 00                  	lis 3, 0
     284: 93 a1 00 34                  	stw 29, 52(1)
     288: 3b a3 00 00                  	addi 29, 3, 0
     28c: 80 7d 00 10                  	lwz 3, 16(29)
     290: 93 81 00 30                  	stw 28, 48(1)
     294: 1c 63 00 18                  	mulli 3, 3, 24
     298: 83 9d 00 20                  	lwz 28, 32(29)
     29c: 7c 7c 1a 14                  	add 3, 28, 3
     2a0: 92 a1 00 14                  	stw 21, 20(1)
     2a4: 7c 1c 18 40                  	cmplw	28, 3
     2a8: 92 c1 00 18                  	stw 22, 24(1)
     2ac: 92 e1 00 1c                  	stw 23, 28(1)
     2b0: 93 01 00 20                  	stw 24, 32(1)
     2b4: 93 21 00 24                  	stw 25, 36(1)
     2b8: 93 41 00 28                  	stw 26, 40(1)
     2bc: 93 61 00 2c                  	stw 27, 44(1)
     2c0: 93 c1 00 38                  	stw 30, 56(1)
     2c4: 40 80 00 ac                  	bf	0, .+172
     2c8: 3c 60 00 00                  	lis 3, 0
     2cc: 3c 80 00 00                  	lis 4, 0
     2d0: 3c a0 81 00                  	lis 5, -32512
     2d4: 3c c0 00 00                  	lis 6, 0
     2d8: 3b 7c ff e8                  	addi 27, 28, -24
     2dc: 3b 40 00 00                  	li 26, 0
     2e0: 3b 23 00 00                  	addi 25, 3, 0
     2e4: 3b 04 00 00                  	addi 24, 4, 0
     2e8: 60 b7 02 00                  	ori 23, 5, 512
     2ec: 3b c6 00 00                  	addi 30, 6, 0
     2f0: 80 79 00 18                  	lwz 3, 24(25)
     2f4: 38 80 0d c0                  	li 4, 3520
     2f8: 38 a0 00 28                  	li 5, 40
     2fc: 82 db 00 1c                  	lwz 22, 28(27)
     300: 82 bb 00 24                  	lwz 21, 36(27)
     304: 48 00 00 01                  	bl .+0
     308: 7c 64 1b 78                  	mr 4, 3
     30c: 7c 63 00 34                  	cntlzw	3, 3
     310: 28 04 00 00                  	cmplwi	4, 0
     314: 54 63 df fe                  	rlwinm 3, 3, 27, 31, 31
     318: 0f 03 00 00                  	twnei	3, 0
     31c: 41 82 00 34                  	bt	2, .+52
     320: 7c 76 aa 14                  	add 3, 22, 21
     324: 93 04 00 10                  	stw 24, 16(4)
     328: 38 63 ff ff                  	addi 3, 3, -1
     32c: 90 64 00 0c                  	stw 3, 12(4)
     330: 7f c3 f3 78                  	mr 3, 30
     334: 93 44 00 00                  	stw 26, 0(4)
     338: 92 c4 00 04                  	stw 22, 4(4)
     33c: 93 44 00 08                  	stw 26, 8(4)
     340: 92 e4 00 14                  	stw 23, 20(4)
     344: 48 00 00 01                  	bl .+0
     348: 54 63 0f fe                  	srwi 3, 3, 31
     34c: 0f 03 00 00                  	twnei	3, 0
     350: 80 7d 00 10                  	lwz 3, 16(29)
     354: 3b 9c 00 18                  	addi 28, 28, 24
     358: 3b 7b 00 18                  	addi 27, 27, 24
     35c: 80 9d 00 20                  	lwz 4, 32(29)
     360: 1c 63 00 18                  	mulli 3, 3, 24
     364: 7c 64 1a 14                  	add 3, 4, 3
     368: 7c 1c 18 40                  	cmplw	28, 3
     36c: 41 80 ff 84                  	bt	0, .+65412
     370: 38 60 00 00                  	li 3, 0
     374: 83 c1 00 38                  	lwz 30, 56(1)
     378: 83 a1 00 34                  	lwz 29, 52(1)
     37c: 83 81 00 30                  	lwz 28, 48(1)
     380: 83 61 00 2c                  	lwz 27, 44(1)
     384: 83 41 00 28                  	lwz 26, 40(1)
     388: 83 21 00 24                  	lwz 25, 36(1)
     38c: 83 01 00 20                  	lwz 24, 32(1)
     390: 82 e1 00 1c                  	lwz 23, 28(1)
     394: 82 c1 00 18                  	lwz 22, 24(1)
     398: 82 a1 00 14                  	lwz 21, 20(1)
     39c: 80 01 00 44                  	lwz 0, 68(1)
     3a0: 38 21 00 40                  	addi 1, 1, 64
     3a4: 7c 08 03 a6                  	mtlr 0
     3a8: 4e 80 00 20                  	blr

[-- Attachment #5: mem-diff.txt --]
[-- Type: text/plain, Size: 45633 bytes --]


mem-working.o:	file format ELF32-ppc			      |	mem-broken.o:	file format ELF32-ppc


Disassembly of section .text:					Disassembly of section .text:

00000000 phys_mem_access_prot:					00000000 phys_mem_access_prot:
       0: 7c 08 02 a6                  	mflr 0			       0: 7c 08 02 a6                  	mflr 0
       4: 90 01 00 04                  	stw 0, 4(1)		       4: 90 01 00 04                  	stw 0, 4(1)
       8: 94 21 ff e0                  	stwu 1, -32(1)		       8: 94 21 ff e0                  	stwu 1, -32(1)
       c: 93 a1 00 14                  	stw 29, 20(1)		       c: 93 a1 00 14                  	stw 29, 20(1)
      10: 7c fd 3b 78                  	mr 29, 7		      10: 7c fd 3b 78                  	mr 29, 7
      14: 3c e0 00 00                  	lis 7, 0		      14: 3c e0 00 00                  	lis 7, 0
      18: 38 e7 00 00                  	addi 7, 7, 0		      18: 38 e7 00 00                  	addi 7, 7, 0
      1c: 81 07 00 a0                  	lwz 8, 160(7)		      1c: 81 07 00 a0                  	lwz 8, 160(7)
      20: 93 c1 00 18                  	stw 30, 24(1)		      20: 93 c1 00 18                  	stw 30, 24(1)
      24: 28 08 00 00                  	cmplwi	8, 0		      24: 28 08 00 00                  	cmplwi	8, 0
      28: 7c 7e 1b 78                  	mr 30, 3		      28: 7c 7e 1b 78                  	mr 30, 3
      2c: 41 82 00 20                  	bt	2, .+32		      2c: 41 82 00 20                  	bt	2, .+32
      30: 80 7d 00 00                  	lwz 3, 0(29)		      30: 80 7d 00 00                  	lwz 3, 0(29)
      34: 38 e1 00 08                  	addi 7, 1, 8		      34: 38 e1 00 08                  	addi 7, 1, 8
      38: 7d 09 03 a6                  	mtctr 8			      38: 7d 09 03 a6                  	mtctr 8
      3c: 90 61 00 08                  	stw 3, 8(1)		      3c: 90 61 00 08                  	stw 3, 8(1)
      40: 7f c3 f3 78                  	mr 3, 30		      40: 7f c3 f3 78                  	mr 3, 30
      44: 4e 80 04 21                  	bctrl			      44: 4e 80 04 21                  	bctrl
      48: 48 00 00 28                  	b .+40			      48: 48 00 00 28                  	b .+40
      4c: 7c a3 2b 78                  	mr 3, 5			      4c: 7c a3 2b 78                  	mr 3, 5
      50: 48 00 00 01                  	bl .+0			      50: 48 00 00 01                  	bl .+0
      54: 28 03 00 00                  	cmplwi	3, 0		      54: 28 03 00 00                  	cmplwi	3, 0
      58: 80 7d 00 00                  	lwz 3, 0(29)		      58: 80 7d 00 00                  	lwz 3, 0(29)
      5c: 40 82 00 10                  	bf	2, .+16		      5c: 40 82 00 10                  	bf	2, .+16
      60: 54 63 06 26                  	rlwinm 3, 3, 0, 24, 1	      60: 54 63 06 26                  	rlwinm 3, 3, 0, 24, 1
      64: 60 63 05 00                  	ori 3, 3, 1280		      64: 60 63 05 00                  	ori 3, 3, 1280
      68: 90 7d 00 00                  	stw 3, 0(29)		      68: 90 7d 00 00                  	stw 3, 0(29)
      6c: 90 7e 00 00                  	stw 3, 0(30)		      6c: 90 7e 00 00                  	stw 3, 0(30)
      70: 83 c1 00 18                  	lwz 30, 24(1)		      70: 83 c1 00 18                  	lwz 30, 24(1)
      74: 83 a1 00 14                  	lwz 29, 20(1)		      74: 83 a1 00 14                  	lwz 29, 20(1)
      78: 80 01 00 24                  	lwz 0, 36(1)		      78: 80 01 00 24                  	lwz 0, 36(1)
      7c: 38 21 00 20                  	addi 1, 1, 32		      7c: 38 21 00 20                  	addi 1, 1, 32
      80: 7c 08 03 a6                  	mtlr 0			      80: 7c 08 03 a6                  	mtlr 0
      84: 4e 80 00 20                  	blr			      84: 4e 80 00 20                  	blr

00000088 free_initmem:						00000088 free_initmem:
      88: 7c 08 02 a6                  	mflr 0			      88: 7c 08 02 a6                  	mflr 0
      8c: 90 01 00 04                  	stw 0, 4(1)		      8c: 90 01 00 04                  	stw 0, 4(1)
      90: 94 21 ff f0                  	stwu 1, -16(1)		      90: 94 21 ff f0                  	stwu 1, -16(1)
      94: 3c 60 00 00                  	lis 3, 0		      94: 3c 60 00 00                  	lis 3, 0
      98: 3c 80 00 00                  	lis 4, 0		      98: 3c 80 00 00                  	lis 4, 0
      9c: 38 63 00 00                  	addi 3, 3, 0		      9c: 38 63 00 00                  	addi 3, 3, 0
      a0: 38 84 00 00                  	addi 4, 4, 0		      a0: 38 84 00 00                  	addi 4, 4, 0
      a4: 90 83 00 60                  	stw 4, 96(3)		      a4: 90 83 00 60                  	stw 4, 96(3)
      a8: 48 00 00 01                  	bl .+0			      a8: 48 00 00 01                  	bl .+0
      ac: 3c 60 00 00                  	lis 3, 0		      ac: 3c 60 00 00                  	lis 3, 0
      b0: 38 80 00 01                  	li 4, 1			      b0: 38 80 00 01                  	li 4, 1
      b4: 98 83 00 00                  	stb 4, 0(3)		      b4: 98 83 00 00                  	stb 4, 0(3)
      b8: 3c 60 00 00                  	lis 3, 0		      b8: 3c 60 00 00                  	lis 3, 0
      bc: 3c 80 00 00                  	lis 4, 0		      bc: 3c 80 00 00                  	lis 4, 0
      c0: 3c a0 00 00                  	lis 5, 0		      c0: 3c a0 00 00                  	lis 5, 0
      c4: 38 63 00 00                  	addi 3, 3, 0		      c4: 38 63 00 00                  	addi 3, 3, 0
      c8: 38 84 00 00                  	addi 4, 4, 0		      c8: 38 84 00 00                  	addi 4, 4, 0
      cc: 38 c5 00 00                  	addi 6, 5, 0		      cc: 38 c5 00 00                  	addi 6, 5, 0
      d0: 38 a0 00 cc                  	li 5, 204		      d0: 38 a0 00 cc                  	li 5, 204
      d4: 48 00 00 01                  	bl .+0			      d4: 48 00 00 01                  	bl .+0
      d8: 80 01 00 14                  	lwz 0, 20(1)		      d8: 80 01 00 14                  	lwz 0, 20(1)
      dc: 38 21 00 10                  	addi 1, 1, 16		      dc: 38 21 00 10                  	addi 1, 1, 16
      e0: 7c 08 03 a6                  	mtlr 0			      e0: 7c 08 03 a6                  	mtlr 0
      e4: 4e 80 00 20                  	blr			      e4: 4e 80 00 20                  	blr

000000e8 flush_dcache_page:					000000e8 flush_dcache_page:
      e8: 80 83 00 00                  	lwz 4, 0(3)		      e8: 80 83 00 00                  	lwz 4, 0(3)
      ec: 70 84 08 00                  	andi. 4, 4, 2048	      ec: 70 84 08 00                  	andi. 4, 4, 2048
      f0: 4d 82 00 20                  	bclr	12, 2		      f0: 4d 82 00 20                  	bclr	12, 2
      f4: 38 80 08 00                  	li 4, 2048		      f4: 38 80 08 00                  	li 4, 2048
      f8: 7c a0 18 28                  	lwarx 5, 0, 3		      f8: 7c a0 18 28                  	lwarx 5, 0, 3
      fc: 7c a5 20 78                  	andc 5, 5, 4		      fc: 7c a5 20 78                  	andc 5, 5, 4
     100: 7c a0 19 2d                  	stwcx. 5, 0, 3		     100: 7c a0 19 2d                  	stwcx. 5, 0, 3
     104: 40 a2 ff f4                  	bc 5, 2, .+65524	     104: 40 a2 ff f4                  	bc 5, 2, .+65524
     108: 4e 80 00 20                  	blr			     108: 4e 80 00 20                  	blr

0000010c flush_dcache_icache_page:				0000010c flush_dcache_icache_page:
     10c: 7c 08 02 a6                  	mflr 0			     10c: 7c 08 02 a6                  	mflr 0
     110: 90 01 00 04                  	stw 0, 4(1)		     110: 90 01 00 04                  	stw 0, 4(1)
     114: 94 21 ff f0                  	stwu 1, -16(1)		     114: 94 21 ff f0                  	stwu 1, -16(1)
     118: 80 82 00 00                  	lwz 4, 0(2)		     118: 80 82 00 00                  	lwz 4, 0(2)
     11c: 38 84 00 01                  	addi 4, 4, 1		     11c: 38 84 00 01                  	addi 4, 4, 1
     120: 90 82 00 00                  	stw 4, 0(2)		     120: 90 82 00 00                  	stw 4, 0(2)
     124: 80 82 0a e4                  	lwz 4, 2788(2)		     124: 80 82 0a e4                  	lwz 4, 2788(2)
     128: 38 84 00 01                  	addi 4, 4, 1		     128: 38 84 00 01                  	addi 4, 4, 1
     12c: 90 82 0a e4                  	stw 4, 2788(2)		     12c: 90 82 0a e4                  	stw 4, 2788(2)
     130: 3c 80 00 00                  	lis 4, 0		     130: 3c 80 00 00                  	lis 4, 0
     134: 80 84 00 00                  	lwz 4, 0(4)		     134: 80 84 00 00                  	lwz 4, 0(4)
     138: 7c 64 18 50                  	subf 3, 4, 3		     138: 7c 64 18 50                  	subf 3, 4, 3
     13c: 3c 80 cc cc                  	lis 4, -13108		     13c: 3c 80 cc cc                  	lis 4, -13108
     140: 54 63 e8 fe                  	srwi 3, 3, 3		     140: 54 63 e8 fe                  	srwi 3, 3, 3
     144: 60 84 d0 00                  	ori 4, 4, 53248		     144: 60 84 d0 00                  	ori 4, 4, 53248
     148: 7c 63 21 d6                  	mullw 3, 3, 4		     148: 7c 63 21 d6                  	mullw 3, 3, 4
     14c: 3c 63 c0 00                  	addis 3, 3, -16384	     14c: 3c 63 c0 00                  	addis 3, 3, -16384
     150: 48 00 00 01                  	bl .+0			     150: 48 00 00 01                  	bl .+0
     154: 80 62 0a e4                  	lwz 3, 2788(2)		     154: 80 62 0a e4                  	lwz 3, 2788(2)
     158: 38 63 ff ff                  	addi 3, 3, -1		     158: 38 63 ff ff                  	addi 3, 3, -1
     15c: 90 62 0a e4                  	stw 3, 2788(2)		     15c: 90 62 0a e4                  	stw 3, 2788(2)
     160: 80 62 00 00                  	lwz 3, 0(2)		     160: 80 62 00 00                  	lwz 3, 0(2)
     164: 38 63 ff ff                  	addi 3, 3, -1		     164: 38 63 ff ff                  	addi 3, 3, -1
     168: 90 62 00 00                  	stw 3, 0(2)		     168: 90 62 00 00                  	stw 3, 0(2)
     16c: 80 01 00 14                  	lwz 0, 20(1)		     16c: 80 01 00 14                  	lwz 0, 20(1)
     170: 38 21 00 10                  	addi 1, 1, 16		     170: 38 21 00 10                  	addi 1, 1, 16
     174: 7c 08 03 a6                  	mtlr 0			     174: 7c 08 03 a6                  	mtlr 0
     178: 4e 80 00 20                  	blr			     178: 4e 80 00 20                  	blr

0000017c clear_user_page:					0000017c clear_user_page:
     17c: 38 80 00 80                  	li 4, 128	      |	     17c: 94 21 ff f0                  	stwu 1, -16(1)
     180: 7c 89 03 a6                  	mtctr 4		      |	     180: 38 80 00 80                  	li 4, 128
     184: 7c 00 1f ec                  	dcbz 0, 3	      |	     184: 38 63 ff e0                  	addi 3, 3, -32
     188: 38 63 00 20                  	addi 3, 3, 32	      |	     188: 7c 89 03 a6                  	mtctr 4
     18c: 42 00 ff f8                  	bdnz .+65528	      |	     18c: 38 81 00 0f                  	addi 4, 1, 15
     190: 80 65 00 00                  	lwz 3, 0(5)	      |	     190: 8c c3 00 20                  	lbzu 6, 32(3)
     194: 70 63 08 00                  	andi. 3, 3, 2048      |	     194: 98 c1 00 0f                  	stb 6, 15(1)
     198: 4d 82 00 20                  	bclr	12, 2	      |	     198: 7c 00 27 ec                  	dcbz 0, 4
     19c: 38 60 08 00                  	li 3, 2048	      |	     19c: 42 00 ff f4                  	bdnz .+65524
     1a0: 7c 80 28 28                  	lwarx 4, 0, 5	      |	     1a0: 80 65 00 00                  	lwz 3, 0(5)
     1a4: 7c 84 18 78                  	andc 4, 4, 3	      |	     1a4: 70 63 08 00                  	andi. 3, 3, 2048
     1a8: 7c 80 29 2d                  	stwcx. 4, 0, 5	      |	     1a8: 41 82 00 18                  	bt	2, .+24
     1ac: 40 a2 ff f4                  	bc 5, 2, .+65524      |	     1ac: 38 60 08 00                  	li 3, 2048
     1b0: 4e 80 00 20                  	blr		      |	     1b0: 7c 80 28 28                  	lwarx 4, 0, 5
							      |	     1b4: 7c 84 18 78                  	andc 4, 4, 3
000001b4 copy_user_page:				      |	     1b8: 7c 80 29 2d                  	stwcx. 4, 0, 5
     1b4: 7c 08 02 a6                  	mflr 0		      |	     1bc: 40 a2 ff f4                  	bc 5, 2, .+65524
     1b8: 90 01 00 04                  	stw 0, 4(1)	      |	     1c0: 38 21 00 10                  	addi 1, 1, 16
     1bc: 94 21 ff f0                  	stwu 1, -16(1)	      |	     1c4: 4e 80 00 20                  	blr
     1c0: 93 c1 00 08                  	stw 30, 8(1)	      |
     1c4: 7c de 33 78                  	mr 30, 6	      |	000001c8 copy_user_page:
     1c8: 48 00 00 01                  	bl .+0		      |	     1c8: 7c 08 02 a6                  	mflr 0
     1cc: 80 7e 00 00                  	lwz 3, 0(30)	      |	     1cc: 90 01 00 04                  	stw 0, 4(1)
     1d0: 70 63 08 00                  	andi. 3, 3, 2048      |	     1d0: 94 21 ff f0                  	stwu 1, -16(1)
     1d4: 41 82 00 18                  	bt	2, .+24	      |	     1d4: 93 c1 00 08                  	stw 30, 8(1)
     1d8: 38 60 08 00                  	li 3, 2048	      |	     1d8: 7c de 33 78                  	mr 30, 6
     1dc: 7c 80 f0 28                  	lwarx 4, 0, 30	      |	     1dc: 48 00 00 01                  	bl .+0
     1e0: 7c 84 18 78                  	andc 4, 4, 3	      |	     1e0: 80 7e 00 00                  	lwz 3, 0(30)
     1e4: 7c 80 f1 2d                  	stwcx. 4, 0, 30	      |	     1e4: 70 63 08 00                  	andi. 3, 3, 2048
     1e8: 40 a2 ff f4                  	bc 5, 2, .+65524      |	     1e8: 41 82 00 18                  	bt	2, .+24
     1ec: 83 c1 00 08                  	lwz 30, 8(1)	      |	     1ec: 38 60 08 00                  	li 3, 2048
     1f0: 80 01 00 14                  	lwz 0, 20(1)	      |	     1f0: 7c 80 f0 28                  	lwarx 4, 0, 30
     1f4: 38 21 00 10                  	addi 1, 1, 16	      |	     1f4: 7c 84 18 78                  	andc 4, 4, 3
     1f8: 7c 08 03 a6                  	mtlr 0		      |	     1f8: 7c 80 f1 2d                  	stwcx. 4, 0, 30
     1fc: 4e 80 00 20                  	blr		      |	     1fc: 40 a2 ff f4                  	bc 5, 2, .+65524
							      |	     200: 83 c1 00 08                  	lwz 30, 8(1)
00000200 flush_icache_user_range:			      |	     204: 80 01 00 14                  	lwz 0, 20(1)
     200: 7c 08 02 a6                  	mflr 0		      |	     208: 38 21 00 10                  	addi 1, 1, 16
     204: 90 01 00 04                  	stw 0, 4(1)	      |	     20c: 7c 08 03 a6                  	mtlr 0
     208: 94 21 ff e0                  	stwu 1, -32(1)	      |	     210: 4e 80 00 20                  	blr
     20c: 3c 60 00 00                  	lis 3, 0	      |
     210: 93 81 00 10                  	stw 28, 16(1)	      |	00000214 flush_icache_user_range:
     214: 7c 9c 23 78                  	mr 28, 4	      |	     214: 7c 08 02 a6                  	mflr 0
     218: 38 63 00 00                  	addi 3, 3, 0	      |	     218: 90 01 00 04                  	stw 0, 4(1)
     21c: 38 80 00 53                  	li 4, 83	      |	     21c: 94 21 ff e0                  	stwu 1, -32(1)
     220: 93 a1 00 14                  	stw 29, 20(1)	      |	     220: 3c 60 00 00                  	lis 3, 0
     224: 7c bd 2b 78                  	mr 29, 5	      |	     224: 93 81 00 10                  	stw 28, 16(1)
     228: 38 a0 00 00                  	li 5, 0		      |	     228: 7c 9c 23 78                  	mr 28, 4
     22c: 93 c1 00 18                  	stw 30, 24(1)	      |	     22c: 38 63 00 00                  	addi 3, 3, 0
     230: 7c de 33 78                  	mr 30, 6	      |	     230: 38 80 00 53                  	li 4, 83
     234: 48 00 00 01                  	bl .+0		      |	     234: 93 a1 00 14                  	stw 29, 20(1)
     238: 3c 60 00 00                  	lis 3, 0	      |	     238: 7c bd 2b 78                  	mr 29, 5
     23c: 3c 80 cc cc                  	lis 4, -13108	      |	     23c: 38 a0 00 00                  	li 5, 0
     240: 80 63 00 00                  	lwz 3, 0(3)	      |	     240: 93 c1 00 18                  	stw 30, 24(1)
     244: 60 84 d0 00                  	ori 4, 4, 53248	      |	     244: 7c de 33 78                  	mr 30, 6
     248: 7c 63 e0 50                  	subf 3, 3, 28	      |	     248: 48 00 00 01                  	bl .+0
     24c: 54 63 e8 fe                  	srwi 3, 3, 3	      |	     24c: 3c 60 00 00                  	lis 3, 0
     250: 7c 63 21 d6                  	mullw 3, 3, 4	      |	     250: 3c 80 cc cc                  	lis 4, -13108
     254: 57 a4 05 3e                  	clrlwi	4, 29, 20     |	     254: 80 63 00 00                  	lwz 3, 0(3)
     258: 50 64 00 26                  	rlwimi 4, 3, 0, 0, 19 |	     258: 60 84 d0 00                  	ori 4, 4, 53248
     25c: 3c 64 c0 00                  	addis 3, 4, -16384    |	     25c: 7c 63 e0 50                  	subf 3, 3, 28
     260: 7c 83 f2 14                  	add 4, 3, 30	      |	     260: 54 63 e8 fe                  	srwi 3, 3, 3
     264: 48 00 00 01                  	bl .+0		      |	     264: 7c 63 21 d6                  	mullw 3, 3, 4
     268: 83 c1 00 18                  	lwz 30, 24(1)	      |	     268: 57 a4 05 3e                  	clrlwi	4, 29, 20
     26c: 83 a1 00 14                  	lwz 29, 20(1)	      |	     26c: 50 64 00 26                  	rlwimi 4, 3, 0, 0, 19
     270: 83 81 00 10                  	lwz 28, 16(1)	      |	     270: 3c 64 c0 00                  	addis 3, 4, -16384
     274: 80 01 00 24                  	lwz 0, 36(1)	      |	     274: 7c 83 f2 14                  	add 4, 3, 30
     278: 38 21 00 20                  	addi 1, 1, 32	      |	     278: 48 00 00 01                  	bl .+0
     27c: 7c 08 03 a6                  	mtlr 0		      |	     27c: 83 c1 00 18                  	lwz 30, 24(1)
     280: 4e 80 00 20                  	blr		      |	     280: 83 a1 00 14                  	lwz 29, 20(1)
							      |	     284: 83 81 00 10                  	lwz 28, 16(1)
00000284 update_mmu_cache:				      |	     288: 80 01 00 24                  	lwz 0, 36(1)
     284: 4e 80 00 20                  	blr		      |	     28c: 38 21 00 20                  	addi 1, 1, 32
							      |	     290: 7c 08 03 a6                  	mtlr 0
00000288 devmem_is_allowed:				      |	     294: 4e 80 00 20                  	blr
     288: 7c 08 02 a6                  	mflr 0		      |
     28c: 90 01 00 04                  	stw 0, 4(1)	      |	00000298 update_mmu_cache:
     290: 94 21 ff f0                  	stwu 1, -16(1)	      |	     298: 4e 80 00 20                  	blr
     294: 93 c1 00 08                  	stw 30, 8(1)	      |
     298: 7c 7e 1b 78                  	mr 30, 3	      |	0000029c devmem_is_allowed:
     29c: 54 63 65 3e                  	srwi 3, 3, 20	      |	     29c: 7c 08 02 a6                  	mflr 0
     2a0: 57 c4 60 26                  	slwi 4, 30, 12	      |	     2a0: 90 01 00 04                  	stw 0, 4(1)
     2a4: 48 00 00 01                  	bl .+0		      |	     2a4: 94 21 ff f0                  	stwu 1, -16(1)
     2a8: 70 63 00 01                  	andi. 3, 3, 1	      |	     2a8: 93 c1 00 08                  	stw 30, 8(1)
     2ac: 38 60 00 00                  	li 3, 0		      |	     2ac: 7c 7e 1b 78                  	mr 30, 3
     2b0: 41 81 00 14                  	bt	1, .+20	      |	     2b0: 54 63 65 3e                  	srwi 3, 3, 20
     2b4: 7f c3 f3 78                  	mr 3, 30	      |	     2b4: 57 c4 60 26                  	slwi 4, 30, 12
     2b8: 48 00 00 01                  	bl .+0			     2b8: 48 00 00 01                  	bl .+0
     2bc: 7c 63 00 34                  	cntlzw	3, 3	      |	     2bc: 70 63 00 01                  	andi. 3, 3, 1
     2c0: 54 63 df fe                  	rlwinm 3, 3, 27, 31,  |	     2c0: 38 60 00 00                  	li 3, 0
     2c4: 83 c1 00 08                  	lwz 30, 8(1)	      |	     2c4: 41 81 00 14                  	bt	1, .+20
     2c8: 80 01 00 14                  	lwz 0, 20(1)	      |	     2c8: 7f c3 f3 78                  	mr 3, 30
     2cc: 38 21 00 10                  	addi 1, 1, 16	      |	     2cc: 48 00 00 01                  	bl .+0
     2d0: 7c 08 03 a6                  	mtlr 0		      |	     2d0: 7c 63 00 34                  	cntlzw	3, 3
     2d4: 4e 80 00 20                  	blr		      |	     2d4: 54 63 df fe                  	rlwinm 3, 3, 27, 31, 
							      >	     2d8: 83 c1 00 08                  	lwz 30, 8(1)
							      >	     2dc: 80 01 00 14                  	lwz 0, 20(1)
							      >	     2e0: 38 21 00 10                  	addi 1, 1, 16
							      >	     2e4: 7c 08 03 a6                  	mtlr 0
							      >	     2e8: 4e 80 00 20                  	blr

Disassembly of section .init.text:				Disassembly of section .init.text:

00000000 mem_topology_setup:					00000000 mem_topology_setup:
       0: 7c 08 02 a6                  	mflr 0			       0: 7c 08 02 a6                  	mflr 0
       4: 90 01 00 04                  	stw 0, 4(1)		       4: 90 01 00 04                  	stw 0, 4(1)
       8: 94 21 ff f0                  	stwu 1, -16(1)		       8: 94 21 ff f0                  	stwu 1, -16(1)
       c: 48 00 00 01                  	bl .+0			       c: 48 00 00 01                  	bl .+0
      10: 54 84 a0 3e                  	rotlwi	4, 4, 20	      10: 54 84 a0 3e                  	rotlwi	4, 4, 20
      14: 3c a0 00 00                  	lis 5, 0		      14: 3c a0 00 00                  	lis 5, 0
      18: 50 64 a0 16                  	rlwimi 4, 3, 20, 0, 1	      18: 50 64 a0 16                  	rlwimi 4, 3, 20, 0, 1
      1c: 3c 60 00 00                  	lis 3, 0		      1c: 3c 60 00 00                  	lis 3, 0
      20: 90 85 00 00                  	stw 4, 0(5)		      20: 90 85 00 00                  	stw 4, 0(5)
      24: 38 a0 ff ff                  	li 5, -1		      24: 38 a0 ff ff                  	li 5, -1
      28: 90 83 00 00                  	stw 4, 0(3)		      28: 90 83 00 00                  	stw 4, 0(3)
      2c: 3c 60 00 00                  	lis 3, 0		      2c: 3c 60 00 00                  	lis 3, 0
      30: 38 80 00 00                  	li 4, 0			      30: 38 80 00 00                  	li 4, 0
      34: 38 c0 ff ff                  	li 6, -1		      34: 38 c0 ff ff                  	li 6, -1
      38: 39 00 00 00                  	li 8, 0			      38: 39 00 00 00                  	li 8, 0
      3c: 90 83 00 00                  	stw 4, 0(3)		      3c: 90 83 00 00                  	stw 4, 0(3)
      40: 3c 60 00 00                  	lis 3, 0		      40: 3c 60 00 00                  	lis 3, 0
      44: 38 80 00 00                  	li 4, 0			      44: 38 80 00 00                  	li 4, 0
      48: 38 63 00 00                  	addi 3, 3, 0		      48: 38 63 00 00                  	addi 3, 3, 0
      4c: 38 e3 00 10                  	addi 7, 3, 16		      4c: 38 e3 00 10                  	addi 7, 3, 16
      50: 38 60 00 00                  	li 3, 0			      50: 38 60 00 00                  	li 3, 0
      54: 48 00 00 01                  	bl .+0			      54: 48 00 00 01                  	bl .+0
      58: 80 01 00 14                  	lwz 0, 20(1)		      58: 80 01 00 14                  	lwz 0, 20(1)
      5c: 38 21 00 10                  	addi 1, 1, 16		      5c: 38 21 00 10                  	addi 1, 1, 16
      60: 7c 08 03 a6                  	mtlr 0			      60: 7c 08 03 a6                  	mtlr 0
      64: 4e 80 00 20                  	blr			      64: 4e 80 00 20                  	blr

00000068 initmem_init:						00000068 initmem_init:
      68: 7c 08 02 a6                  	mflr 0			      68: 7c 08 02 a6                  	mflr 0
      6c: 90 01 00 04                  	stw 0, 4(1)		      6c: 90 01 00 04                  	stw 0, 4(1)
      70: 94 21 ff f0                  	stwu 1, -16(1)		      70: 94 21 ff f0                  	stwu 1, -16(1)
      74: 38 60 00 00                  	li 3, 0			      74: 38 60 00 00                  	li 3, 0
      78: 48 00 00 01                  	bl .+0			      78: 48 00 00 01                  	bl .+0
      7c: 80 01 00 14                  	lwz 0, 20(1)		      7c: 80 01 00 14                  	lwz 0, 20(1)
      80: 38 21 00 10                  	addi 1, 1, 16		      80: 38 21 00 10                  	addi 1, 1, 16
      84: 7c 08 03 a6                  	mtlr 0			      84: 7c 08 03 a6                  	mtlr 0
      88: 4e 80 00 20                  	blr			      88: 4e 80 00 20                  	blr

0000008c paging_init:						0000008c paging_init:
      8c: 7c 08 02 a6                  	mflr 0			      8c: 7c 08 02 a6                  	mflr 0
      90: 90 01 00 04                  	stw 0, 4(1)		      90: 90 01 00 04                  	stw 0, 4(1)
      94: 94 21 ff d0                  	stwu 1, -48(1)		      94: 94 21 ff d0                  	stwu 1, -48(1)
      98: 93 21 00 14                  	stw 25, 20(1)		      98: 93 21 00 14                  	stw 25, 20(1)
      9c: 93 41 00 18                  	stw 26, 24(1)		      9c: 93 41 00 18                  	stw 26, 24(1)
      a0: 93 61 00 1c                  	stw 27, 28(1)		      a0: 93 61 00 1c                  	stw 27, 28(1)
      a4: 93 81 00 20                  	stw 28, 32(1)		      a4: 93 81 00 20                  	stw 28, 32(1)
      a8: 93 a1 00 24                  	stw 29, 36(1)		      a8: 93 a1 00 24                  	stw 29, 36(1)
      ac: 93 c1 00 28                  	stw 30, 40(1)		      ac: 93 c1 00 28                  	stw 30, 40(1)
      b0: 48 00 00 01                  	bl .+0			      b0: 48 00 00 01                  	bl .+0
      b4: 7c 7e 1b 78                  	mr 30, 3		      b4: 7c 7e 1b 78                  	mr 30, 3
      b8: 7c 9d 23 78                  	mr 29, 4		      b8: 7c 9d 23 78                  	mr 29, 4
      bc: 3f 40 ff fe                  	lis 26, -2		      bc: 3f 40 ff fe                  	lis 26, -2
      c0: 48 00 00 01                  	bl .+0			      c0: 48 00 00 01                  	bl .+0
      c4: 7c 7c 1b 78                  	mr 28, 3		      c4: 7c 7c 1b 78                  	mr 28, 3
      c8: 7c 9b 23 78                  	mr 27, 4		      c8: 7c 9b 23 78                  	mr 27, 4
      cc: 3b 20 00 00                  	li 25, 0		      cc: 3b 20 00 00                  	li 25, 0
      d0: 38 e1 00 08                  	addi 7, 1, 8		      d0: 38 e1 00 08                  	addi 7, 1, 8
      d4: 7f 43 d3 78                  	mr 3, 26		      d4: 7f 43 d3 78                  	mr 3, 26
      d8: 93 21 00 10                  	stw 25, 16(1)		      d8: 93 21 00 10                  	stw 25, 16(1)
      dc: 38 a0 00 00                  	li 5, 0			      dc: 38 a0 00 00                  	li 5, 0
      e0: 38 c0 00 00                  	li 6, 0			      e0: 38 c0 00 00                  	li 6, 0
      e4: 93 21 00 08                  	stw 25, 8(1)		      e4: 93 21 00 08                  	stw 25, 8(1)
      e8: 48 00 00 01                  	bl .+0			      e8: 48 00 00 01                  	bl .+0
      ec: 3b 5a 10 00                  	addi 26, 26, 4096	      ec: 3b 5a 10 00                  	addi 26, 26, 4096
      f0: 2c 1a f0 00                  	cmpwi	26, -4096	      f0: 2c 1a f0 00                  	cmpwi	26, -4096
      f4: 40 82 ff dc                  	bf	2, .+65500	      f4: 40 82 ff dc                  	bf	2, .+65500
      f8: 3c 60 00 00                  	lis 3, 0		      f8: 3c 60 00 00                  	lis 3, 0
      fc: 4c c6 31 82                  	crclr	6		      fc: 4c c6 31 82                  	crclr	6
     100: 7f 85 e3 78                  	mr 5, 28		     100: 7f 85 e3 78                  	mr 5, 28
     104: 38 63 00 00                  	addi 3, 3, 0		     104: 38 63 00 00                  	addi 3, 3, 0
     108: 7f 66 db 78                  	mr 6, 27		     108: 7f 66 db 78                  	mr 6, 27
     10c: 7f c7 f3 78                  	mr 7, 30		     10c: 7f c7 f3 78                  	mr 7, 30
     110: 7f a8 eb 78                  	mr 8, 29		     110: 7f a8 eb 78                  	mr 8, 29
     114: 48 00 00 01                  	bl .+0			     114: 48 00 00 01                  	bl .+0
     118: 7c 7d d8 10                  	subfc 3, 29, 27		     118: 7c 7d d8 10                  	subfc 3, 29, 27
     11c: 4c c6 31 82                  	crclr	6		     11c: 4c c6 31 82                  	crclr	6
     120: 7c be e1 10                  	subfe 5, 30, 28		     120: 7c be e1 10                  	subfe 5, 30, 28
     124: 54 64 60 3e                  	rotlwi	4, 3, 12	     124: 54 64 60 3e                  	rotlwi	4, 3, 12
     128: 3c 60 00 00                  	lis 3, 0		     128: 3c 60 00 00                  	lis 3, 0
     12c: 50 a4 60 26                  	rlwimi 4, 5, 12, 0, 1	     12c: 50 a4 60 26                  	rlwimi 4, 5, 12, 0, 1
     130: 38 63 00 00                  	addi 3, 3, 0		     130: 38 63 00 00                  	addi 3, 3, 0
     134: 48 00 00 01                  	bl .+0			     134: 48 00 00 01                  	bl .+0
     138: 3c 60 00 00                  	lis 3, 0		     138: 3c 60 00 00                  	lis 3, 0
     13c: 80 83 00 00                  	lwz 4, 0(3)		     13c: 80 83 00 00                  	lwz 4, 0(3)
     140: 3c 60 00 00                  	lis 3, 0		     140: 3c 60 00 00                  	lis 3, 0
     144: 94 83 00 00                  	stwu 4, 0(3)		     144: 94 83 00 00                  	stwu 4, 0(3)
     148: 48 00 00 01                  	bl .+0			     148: 48 00 00 01                  	bl .+0
     14c: 83 c1 00 28                  	lwz 30, 40(1)		     14c: 83 c1 00 28                  	lwz 30, 40(1)
     150: 83 a1 00 24                  	lwz 29, 36(1)		     150: 83 a1 00 24                  	lwz 29, 36(1)
     154: 83 81 00 20                  	lwz 28, 32(1)		     154: 83 81 00 20                  	lwz 28, 32(1)
     158: 83 61 00 1c                  	lwz 27, 28(1)		     158: 83 61 00 1c                  	lwz 27, 28(1)
     15c: 83 41 00 18                  	lwz 26, 24(1)		     15c: 83 41 00 18                  	lwz 26, 24(1)
     160: 83 21 00 14                  	lwz 25, 20(1)		     160: 83 21 00 14                  	lwz 25, 20(1)
     164: 80 01 00 34                  	lwz 0, 52(1)		     164: 80 01 00 34                  	lwz 0, 52(1)
     168: 38 21 00 30                  	addi 1, 1, 48		     168: 38 21 00 30                  	addi 1, 1, 48
     16c: 7c 08 03 a6                  	mtlr 0			     16c: 7c 08 03 a6                  	mtlr 0
     170: 4e 80 00 20                  	blr			     170: 4e 80 00 20                  	blr

00000174 mem_init:						00000174 mem_init:
     174: 7c 08 02 a6                  	mflr 0			     174: 7c 08 02 a6                  	mflr 0
     178: 90 01 00 04                  	stw 0, 4(1)		     178: 90 01 00 04                  	stw 0, 4(1)
     17c: 94 21 ff e0                  	stwu 1, -32(1)		     17c: 94 21 ff e0                  	stwu 1, -32(1)
     180: 3c 60 00 00                  	lis 3, 0		     180: 3c 60 00 00                  	lis 3, 0
     184: 3c 80 00 00                  	lis 4, 0		     184: 3c 80 00 00                  	lis 4, 0
     188: 93 81 00 10                  	stw 28, 16(1)		     188: 93 81 00 10                  	stw 28, 16(1)
     18c: 3f 80 00 00                  	lis 28, 0		     18c: 3f 80 00 00                  	lis 28, 0
     190: 80 63 00 00                  	lwz 3, 0(3)		     190: 80 63 00 00                  	lwz 3, 0(3)
     194: 80 84 00 00                  	lwz 4, 0(4)		     194: 80 84 00 00                  	lwz 4, 0(4)
     198: 54 63 60 26                  	slwi 3, 3, 12		     198: 54 63 60 26                  	slwi 3, 3, 12
     19c: 3c 63 c0 00                  	addis 3, 3, -16384	     19c: 3c 63 c0 00                  	addis 3, 3, -16384
     1a0: 90 7c 00 00                  	stw 3, 0(28)		     1a0: 90 7c 00 00                  	stw 3, 0(28)
     1a4: 3c 60 00 00                  	lis 3, 0		     1a4: 3c 60 00 00                  	lis 3, 0
     1a8: 93 a1 00 14                  	stw 29, 20(1)		     1a8: 93 a1 00 14                  	stw 29, 20(1)
     1ac: 93 c1 00 18                  	stw 30, 24(1)		     1ac: 93 c1 00 18                  	stw 30, 24(1)
     1b0: 90 83 00 00                  	stw 4, 0(3)		     1b0: 90 83 00 00                  	stw 4, 0(3)
     1b4: 48 00 00 01                  	bl .+0			     1b4: 48 00 00 01                  	bl .+0
     1b8: 38 60 00 00                  	li 3, 0			     1b8: 38 60 00 00                  	li 3, 0
     1bc: 48 00 00 01                  	bl .+0			     1bc: 48 00 00 01                  	bl .+0
     1c0: 3c 60 00 00                  	lis 3, 0		     1c0: 3c 60 00 00                  	lis 3, 0
     1c4: 4c c6 31 82                  	crclr	6		     1c4: 4c c6 31 82                  	crclr	6
     1c8: 38 63 00 00                  	addi 3, 3, 0		     1c8: 38 63 00 00                  	addi 3, 3, 0
     1cc: 48 00 00 01                  	bl .+0			     1cc: 48 00 00 01                  	bl .+0
     1d0: 3c 80 ff fd                  	lis 4, -3		     1d0: 3c 80 ff fd                  	lis 4, -3
     1d4: 3c 60 00 00                  	lis 3, 0		     1d4: 3c 60 00 00                  	lis 3, 0
     1d8: 4c c6 31 82                  	crclr	6		     1d8: 4c c6 31 82                  	crclr	6
     1dc: 60 9e f0 00                  	ori 30, 4, 61440	     1dc: 60 9e f0 00                  	ori 30, 4, 61440
     1e0: 38 63 00 00                  	addi 3, 3, 0		     1e0: 38 63 00 00                  	addi 3, 3, 0
     1e4: 7f c4 f3 78                  	mr 4, 30		     1e4: 7f c4 f3 78                  	mr 4, 30
     1e8: 38 a0 f0 00                  	li 5, -4096		     1e8: 38 a0 f0 00                  	li 5, -4096
     1ec: 48 00 00 01                  	bl .+0			     1ec: 48 00 00 01                  	bl .+0
     1f0: 3c 80 ff dd                  	lis 4, -35		     1f0: 3c 80 ff dd                  	lis 4, -35
     1f4: 3c 60 00 00                  	lis 3, 0		     1f4: 3c 60 00 00                  	lis 3, 0
     1f8: 4c c6 31 82                  	crclr	6		     1f8: 4c c6 31 82                  	crclr	6
     1fc: 60 9d f0 00                  	ori 29, 4, 61440	     1fc: 60 9d f0 00                  	ori 29, 4, 61440
     200: 38 63 00 00                  	addi 3, 3, 0		     200: 38 63 00 00                  	addi 3, 3, 0
     204: 7f a4 eb 78                  	mr 4, 29		     204: 7f a4 eb 78                  	mr 4, 29
     208: 7f c5 f3 78                  	mr 5, 30		     208: 7f c5 f3 78                  	mr 5, 30
     20c: 48 00 00 01                  	bl .+0			     20c: 48 00 00 01                  	bl .+0
     210: 3f c0 00 00                  	lis 30, 0		     210: 3f c0 00 00                  	lis 30, 0
     214: 3c 60 00 00                  	lis 3, 0		     214: 3c 60 00 00                  	lis 3, 0
     218: 4c c6 31 82                  	crclr	6		     218: 4c c6 31 82                  	crclr	6
     21c: 80 9e 00 00                  	lwz 4, 0(30)		     21c: 80 9e 00 00                  	lwz 4, 0(30)
     220: 38 63 00 00                  	addi 3, 3, 0		     220: 38 63 00 00                  	addi 3, 3, 0
     224: 7f a5 eb 78                  	mr 5, 29		     224: 7f a5 eb 78                  	mr 5, 29
     228: 48 00 00 01                  	bl .+0			     228: 48 00 00 01                  	bl .+0
     22c: 80 7c 00 00                  	lwz 3, 0(28)		     22c: 80 7c 00 00                  	lwz 3, 0(28)
     230: 3c 80 01 00                  	lis 4, 256		     230: 3c 80 01 00                  	lis 4, 256
     234: 4c c6 31 82                  	crclr	6		     234: 4c c6 31 82                  	crclr	6
     238: 38 63 ff ff                  	addi 3, 3, -1		     238: 38 63 ff ff                  	addi 3, 3, -1
     23c: 80 be 00 00                  	lwz 5, 0(30)		     23c: 80 be 00 00                  	lwz 5, 0(30)
     240: 3c 63 10 00                  	addis 3, 3, 4096	     240: 3c 63 10 00                  	addis 3, 3, 4096
     244: 54 63 00 06                  	rlwinm 3, 3, 0, 0, 3	     244: 54 63 00 06                  	rlwinm 3, 3, 0, 0, 3
     248: 50 64 02 0c                  	rlwimi 4, 3, 0, 8, 6	     248: 50 64 02 0c                  	rlwimi 4, 3, 0, 8, 6
     24c: 3c 60 00 00                  	lis 3, 0		     24c: 3c 60 00 00                  	lis 3, 0
     250: 38 63 00 00                  	addi 3, 3, 0		     250: 38 63 00 00                  	addi 3, 3, 0
     254: 48 00 00 01                  	bl .+0			     254: 48 00 00 01                  	bl .+0
     258: 83 c1 00 18                  	lwz 30, 24(1)		     258: 83 c1 00 18                  	lwz 30, 24(1)
     25c: 83 a1 00 14                  	lwz 29, 20(1)		     25c: 83 a1 00 14                  	lwz 29, 20(1)
     260: 83 81 00 10                  	lwz 28, 16(1)		     260: 83 81 00 10                  	lwz 28, 16(1)
     264: 80 01 00 24                  	lwz 0, 36(1)		     264: 80 01 00 24                  	lwz 0, 36(1)
     268: 38 21 00 20                  	addi 1, 1, 32		     268: 38 21 00 20                  	addi 1, 1, 32
     26c: 7c 08 03 a6                  	mtlr 0			     26c: 7c 08 03 a6                  	mtlr 0
     270: 4e 80 00 20                  	blr			     270: 4e 80 00 20                  	blr

00000274 add_system_ram_resources:				00000274 add_system_ram_resources:
     274: 7c 08 02 a6                  	mflr 0			     274: 7c 08 02 a6                  	mflr 0
     278: 90 01 00 04                  	stw 0, 4(1)		     278: 90 01 00 04                  	stw 0, 4(1)
     27c: 94 21 ff c0                  	stwu 1, -64(1)		     27c: 94 21 ff c0                  	stwu 1, -64(1)
     280: 3c 60 00 00                  	lis 3, 0		     280: 3c 60 00 00                  	lis 3, 0
     284: 93 a1 00 34                  	stw 29, 52(1)		     284: 93 a1 00 34                  	stw 29, 52(1)
     288: 3b a3 00 00                  	addi 29, 3, 0		     288: 3b a3 00 00                  	addi 29, 3, 0
     28c: 80 7d 00 10                  	lwz 3, 16(29)		     28c: 80 7d 00 10                  	lwz 3, 16(29)
     290: 93 81 00 30                  	stw 28, 48(1)		     290: 93 81 00 30                  	stw 28, 48(1)
     294: 1c 63 00 18                  	mulli 3, 3, 24		     294: 1c 63 00 18                  	mulli 3, 3, 24
     298: 83 9d 00 20                  	lwz 28, 32(29)		     298: 83 9d 00 20                  	lwz 28, 32(29)
     29c: 7c 7c 1a 14                  	add 3, 28, 3		     29c: 7c 7c 1a 14                  	add 3, 28, 3
     2a0: 92 a1 00 14                  	stw 21, 20(1)		     2a0: 92 a1 00 14                  	stw 21, 20(1)
     2a4: 7c 1c 18 40                  	cmplw	28, 3		     2a4: 7c 1c 18 40                  	cmplw	28, 3
     2a8: 92 c1 00 18                  	stw 22, 24(1)		     2a8: 92 c1 00 18                  	stw 22, 24(1)
     2ac: 92 e1 00 1c                  	stw 23, 28(1)		     2ac: 92 e1 00 1c                  	stw 23, 28(1)
     2b0: 93 01 00 20                  	stw 24, 32(1)		     2b0: 93 01 00 20                  	stw 24, 32(1)
     2b4: 93 21 00 24                  	stw 25, 36(1)		     2b4: 93 21 00 24                  	stw 25, 36(1)
     2b8: 93 41 00 28                  	stw 26, 40(1)		     2b8: 93 41 00 28                  	stw 26, 40(1)
     2bc: 93 61 00 2c                  	stw 27, 44(1)		     2bc: 93 61 00 2c                  	stw 27, 44(1)
     2c0: 93 c1 00 38                  	stw 30, 56(1)		     2c0: 93 c1 00 38                  	stw 30, 56(1)
     2c4: 40 80 00 ac                  	bf	0, .+172	     2c4: 40 80 00 ac                  	bf	0, .+172
     2c8: 3c 60 00 00                  	lis 3, 0		     2c8: 3c 60 00 00                  	lis 3, 0
     2cc: 3c 80 00 00                  	lis 4, 0		     2cc: 3c 80 00 00                  	lis 4, 0
     2d0: 3c a0 81 00                  	lis 5, -32512		     2d0: 3c a0 81 00                  	lis 5, -32512
     2d4: 3c c0 00 00                  	lis 6, 0		     2d4: 3c c0 00 00                  	lis 6, 0
     2d8: 3b 7c ff e8                  	addi 27, 28, -24	     2d8: 3b 7c ff e8                  	addi 27, 28, -24
     2dc: 3b 40 00 00                  	li 26, 0		     2dc: 3b 40 00 00                  	li 26, 0
     2e0: 3b 23 00 00                  	addi 25, 3, 0		     2e0: 3b 23 00 00                  	addi 25, 3, 0
     2e4: 3b 04 00 00                  	addi 24, 4, 0		     2e4: 3b 04 00 00                  	addi 24, 4, 0
     2e8: 60 b7 02 00                  	ori 23, 5, 512		     2e8: 60 b7 02 00                  	ori 23, 5, 512
     2ec: 3b c6 00 00                  	addi 30, 6, 0		     2ec: 3b c6 00 00                  	addi 30, 6, 0
     2f0: 80 79 00 18                  	lwz 3, 24(25)		     2f0: 80 79 00 18                  	lwz 3, 24(25)
     2f4: 38 80 0d c0                  	li 4, 3520		     2f4: 38 80 0d c0                  	li 4, 3520
     2f8: 38 a0 00 28                  	li 5, 40		     2f8: 38 a0 00 28                  	li 5, 40
     2fc: 82 db 00 1c                  	lwz 22, 28(27)		     2fc: 82 db 00 1c                  	lwz 22, 28(27)
     300: 82 bb 00 24                  	lwz 21, 36(27)		     300: 82 bb 00 24                  	lwz 21, 36(27)
     304: 48 00 00 01                  	bl .+0			     304: 48 00 00 01                  	bl .+0
     308: 7c 64 1b 78                  	mr 4, 3			     308: 7c 64 1b 78                  	mr 4, 3
     30c: 7c 63 00 34                  	cntlzw	3, 3		     30c: 7c 63 00 34                  	cntlzw	3, 3
     310: 28 04 00 00                  	cmplwi	4, 0		     310: 28 04 00 00                  	cmplwi	4, 0
     314: 54 63 df fe                  	rlwinm 3, 3, 27, 31, 	     314: 54 63 df fe                  	rlwinm 3, 3, 27, 31, 
     318: 0f 03 00 00                  	twnei	3, 0		     318: 0f 03 00 00                  	twnei	3, 0
     31c: 41 82 00 34                  	bt	2, .+52		     31c: 41 82 00 34                  	bt	2, .+52
     320: 7c 76 aa 14                  	add 3, 22, 21		     320: 7c 76 aa 14                  	add 3, 22, 21
     324: 93 04 00 10                  	stw 24, 16(4)		     324: 93 04 00 10                  	stw 24, 16(4)
     328: 38 63 ff ff                  	addi 3, 3, -1		     328: 38 63 ff ff                  	addi 3, 3, -1
     32c: 90 64 00 0c                  	stw 3, 12(4)		     32c: 90 64 00 0c                  	stw 3, 12(4)
     330: 7f c3 f3 78                  	mr 3, 30		     330: 7f c3 f3 78                  	mr 3, 30
     334: 93 44 00 00                  	stw 26, 0(4)		     334: 93 44 00 00                  	stw 26, 0(4)
     338: 92 c4 00 04                  	stw 22, 4(4)		     338: 92 c4 00 04                  	stw 22, 4(4)
     33c: 93 44 00 08                  	stw 26, 8(4)		     33c: 93 44 00 08                  	stw 26, 8(4)
     340: 92 e4 00 14                  	stw 23, 20(4)		     340: 92 e4 00 14                  	stw 23, 20(4)
     344: 48 00 00 01                  	bl .+0			     344: 48 00 00 01                  	bl .+0
     348: 54 63 0f fe                  	srwi 3, 3, 31		     348: 54 63 0f fe                  	srwi 3, 3, 31
     34c: 0f 03 00 00                  	twnei	3, 0		     34c: 0f 03 00 00                  	twnei	3, 0
     350: 80 7d 00 10                  	lwz 3, 16(29)		     350: 80 7d 00 10                  	lwz 3, 16(29)
     354: 3b 9c 00 18                  	addi 28, 28, 24		     354: 3b 9c 00 18                  	addi 28, 28, 24
     358: 3b 7b 00 18                  	addi 27, 27, 24		     358: 3b 7b 00 18                  	addi 27, 27, 24
     35c: 80 9d 00 20                  	lwz 4, 32(29)		     35c: 80 9d 00 20                  	lwz 4, 32(29)
     360: 1c 63 00 18                  	mulli 3, 3, 24		     360: 1c 63 00 18                  	mulli 3, 3, 24
     364: 7c 64 1a 14                  	add 3, 4, 3		     364: 7c 64 1a 14                  	add 3, 4, 3
     368: 7c 1c 18 40                  	cmplw	28, 3		     368: 7c 1c 18 40                  	cmplw	28, 3
     36c: 41 80 ff 84                  	bt	0, .+65412	     36c: 41 80 ff 84                  	bt	0, .+65412
     370: 38 60 00 00                  	li 3, 0			     370: 38 60 00 00                  	li 3, 0
     374: 83 c1 00 38                  	lwz 30, 56(1)		     374: 83 c1 00 38                  	lwz 30, 56(1)
     378: 83 a1 00 34                  	lwz 29, 52(1)		     378: 83 a1 00 34                  	lwz 29, 52(1)
     37c: 83 81 00 30                  	lwz 28, 48(1)		     37c: 83 81 00 30                  	lwz 28, 48(1)
     380: 83 61 00 2c                  	lwz 27, 44(1)		     380: 83 61 00 2c                  	lwz 27, 44(1)
     384: 83 41 00 28                  	lwz 26, 40(1)		     384: 83 41 00 28                  	lwz 26, 40(1)
     388: 83 21 00 24                  	lwz 25, 36(1)		     388: 83 21 00 24                  	lwz 25, 36(1)
     38c: 83 01 00 20                  	lwz 24, 32(1)		     38c: 83 01 00 20                  	lwz 24, 32(1)
     390: 82 e1 00 1c                  	lwz 23, 28(1)		     390: 82 e1 00 1c                  	lwz 23, 28(1)
     394: 82 c1 00 18                  	lwz 22, 24(1)		     394: 82 c1 00 18                  	lwz 22, 24(1)
     398: 82 a1 00 14                  	lwz 21, 20(1)		     398: 82 a1 00 14                  	lwz 21, 20(1)
     39c: 80 01 00 44                  	lwz 0, 68(1)		     39c: 80 01 00 44                  	lwz 0, 68(1)
     3a0: 38 21 00 40                  	addi 1, 1, 64		     3a0: 38 21 00 40                  	addi 1, 1, 64
     3a4: 7c 08 03 a6                  	mtlr 0			     3a4: 7c 08 03 a6                  	mtlr 0
     3a8: 4e 80 00 20                  	blr			     3a8: 4e 80 00 20                  	blr

^ permalink raw reply related

* Re: [PATCH v2] powerpc: slightly improve cache helpers
From: Segher Boessenkool @ 2019-07-21 18:01 UTC (permalink / raw)
  To: Nathan Chancellor
  Cc: linux-kernel, clang-built-linux, Paul Mackerras, linuxppc-dev
In-Reply-To: <20190721075846.GA97701@archlinux-threadripper>

On Sun, Jul 21, 2019 at 12:58:46AM -0700, Nathan Chancellor wrote:
> I have attached the disassembly of arch/powerpc/kernel/mem.o with
> clear_page (working) and broken_clear_page (broken), along with the side
> by side diff. My assembly knowledge is fairly limited as it stands and
> it is certainly not up to snuff on PowerPC so I have no idea what I am
> looking for. Please let me know if anything immediately looks off or if
> there is anything else I can do to help out.

You might want to use a disassembler that shows most simplified mnemonics,
and you crucially should show the relocations.  "objdump -dr" works nicely.

> 0000017c clear_user_page:
>      17c: 38 80 00 80                  	li 4, 128
>      180: 7c 89 03 a6                  	mtctr 4
>      184: 7c 00 1f ec                  	dcbz 0, 3
>      188: 38 63 00 20                  	addi 3, 3, 32
>      18c: 42 00 ff f8                  	bdnz .+65528

That offset is incorrectly disassembled, btw (it's a signed field, not
unsigned).

> 0000017c clear_user_page:
>      17c: 94 21 ff f0                  	stwu 1, -16(1)
>      180: 38 80 00 80                  	li 4, 128
>      184: 38 63 ff e0                  	addi 3, 3, -32
>      188: 7c 89 03 a6                  	mtctr 4
>      18c: 38 81 00 0f                  	addi 4, 1, 15
>      190: 8c c3 00 20                  	lbzu 6, 32(3)
>      194: 98 c1 00 0f                  	stb 6, 15(1)
>      198: 7c 00 27 ec                  	dcbz 0, 4
>      19c: 42 00 ff f4                  	bdnz .+65524

Uh, yeah, well, I have no idea what clang tried here, but that won't
work.  It's copying a byte from each target cache line to the stack,
and then does clears the cache line containing that byte on the stack.

I *guess* this is about "Z" and not about "%y", but you'll have to ask
the clang people.

Or it may be that they do not treat inline asm operands as lvalues
properly?  That rings some bells.  Yeah that looks like it.


Segher

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox