Merge pull request #2208 from kwilczynski/feature/use-latest-securejoin-for-release-1.51
[release-1.51] Use newer release of securejoin package to secure userns paths
This commit is contained in:
commit
e0f022605d
4
go.mod
4
go.mod
|
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/Microsoft/go-winio v0.6.1
|
||||
github.com/Microsoft/hcsshim v0.12.0-rc.1
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1
|
||||
github.com/cyphar/filepath-securejoin v0.2.4
|
||||
github.com/cyphar/filepath-securejoin v0.3.6
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/google/go-intervals v0.0.2
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
|
|
@ -27,7 +27,7 @@ require (
|
|||
github.com/tchap/go-patricia/v2 v2.3.1
|
||||
github.com/ulikunitz/xz v0.5.11
|
||||
github.com/vbatts/tar-split v0.11.5
|
||||
golang.org/x/sys v0.14.0
|
||||
golang.org/x/sys v0.18.0
|
||||
gotest.tools v2.2.0+incompatible
|
||||
)
|
||||
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -15,8 +15,8 @@ github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXT
|
|||
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -143,8 +143,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
|
|
|
|||
11
userns.go
11
userns.go
|
|
@ -334,16 +334,11 @@ func getAutoUserNSIDMappings(
|
|||
|
||||
// Securely open (read-only) a file in a container mount.
|
||||
func secureOpen(containerMount, file string) (*os.File, error) {
|
||||
filePath, err := securejoin.SecureJoin(containerMount, file)
|
||||
tmpFile, err := securejoin.OpenInRoot(containerMount, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
|
||||
flags := unix.O_PATH | unix.O_CLOEXEC | unix.O_RDONLY
|
||||
fileHandle, err := os.OpenFile(filePath, flags, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fileHandle, nil
|
||||
return securejoin.Reopen(tmpFile, unix.O_RDONLY)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
# Changelog #
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased] ##
|
||||
|
||||
## [0.3.6] - 2024-12-17 ##
|
||||
|
||||
### Compatibility ###
|
||||
- The minimum Go version requirement for `filepath-securejoin` is now Go 1.18
|
||||
(we use generics internally).
|
||||
|
||||
For reference, `filepath-securejoin@v0.3.0` somewhat-arbitrarily bumped the
|
||||
Go version requirement to 1.21.
|
||||
|
||||
While we did make some use of Go 1.21 stdlib features (and in principle Go
|
||||
versions <= 1.21 are no longer even supported by upstream anymore), some
|
||||
downstreams have complained that the version bump has meant that they have to
|
||||
do workarounds when backporting fixes that use the new `filepath-securejoin`
|
||||
API onto old branches. This is not an ideal situation, but since using this
|
||||
library is probably better for most downstreams than a hand-rolled
|
||||
workaround, we now have compatibility shims that allow us to build on older
|
||||
Go versions.
|
||||
- Lower minimum version requirement for `golang.org/x/sys` to `v0.18.0` (we
|
||||
need the wrappers for `fsconfig(2)`), which should also make backporting
|
||||
patches to older branches easier.
|
||||
|
||||
## [0.3.5] - 2024-12-06 ##
|
||||
|
||||
### Fixed ###
|
||||
- `MkdirAll` will now no longer return an `EEXIST` error if two racing
|
||||
processes are creating the same directory. We will still verify that the path
|
||||
is a directory, but this will avoid spurious errors when multiple threads or
|
||||
programs are trying to `MkdirAll` the same path. opencontainers/runc#4543
|
||||
|
||||
## [0.3.4] - 2024-10-09 ##
|
||||
|
||||
### Fixed ###
|
||||
- Previously, some testing mocks we had resulted in us doing `import "testing"`
|
||||
in non-`_test.go` code, which made some downstreams like Kubernetes unhappy.
|
||||
This has been fixed. (#32)
|
||||
|
||||
## [0.3.3] - 2024-09-30 ##
|
||||
|
||||
### Fixed ###
|
||||
- The mode and owner verification logic in `MkdirAll` has been removed. This
|
||||
was originally intended to protect against some theoretical attacks but upon
|
||||
further consideration these protections don't actually buy us anything and
|
||||
they were causing spurious errors with more complicated filesystem setups.
|
||||
- The "is the created directory empty" logic in `MkdirAll` has also been
|
||||
removed. This was not causing us issues yet, but some pseudofilesystems (such
|
||||
as `cgroup`) create non-empty directories and so this logic would've been
|
||||
wrong for such cases.
|
||||
|
||||
## [0.3.2] - 2024-09-13 ##
|
||||
|
||||
### Changed ###
|
||||
- Passing the `S_ISUID` or `S_ISGID` modes to `MkdirAllInRoot` will now return
|
||||
an explicit error saying that those bits are ignored by `mkdirat(2)`. In the
|
||||
past a different error was returned, but since the silent ignoring behaviour
|
||||
is codified in the man pages a more explicit error seems apt. While silently
|
||||
ignoring these bits would be the most compatible option, it could lead to
|
||||
users thinking their code sets these bits when it doesn't. Programs that need
|
||||
to deal with compatibility can mask the bits themselves. (#23, #25)
|
||||
|
||||
### Fixed ###
|
||||
- If a directory has `S_ISGID` set, then all child directories will have
|
||||
`S_ISGID` set when created and a different gid will be used for any inode
|
||||
created under the directory. Previously, the "expected owner and mode"
|
||||
validation in `securejoin.MkdirAll` did not correctly handle this. We now
|
||||
correctly handle this case. (#24, #25)
|
||||
|
||||
## [0.3.1] - 2024-07-23 ##
|
||||
|
||||
### Changed ###
|
||||
- By allowing `Open(at)InRoot` to opt-out of the extra work done by `MkdirAll`
|
||||
to do the necessary "partial lookups", `Open(at)InRoot` now does less work
|
||||
for both implementations (resulting in a many-fold decrease in the number of
|
||||
operations for `openat2`, and a modest improvement for non-`openat2`) and is
|
||||
far more guaranteed to match the correct `openat2(RESOLVE_IN_ROOT)`
|
||||
behaviour.
|
||||
- We now use `readlinkat(fd, "")` where possible. For `Open(at)InRoot` this
|
||||
effectively just means that we no longer risk getting spurious errors during
|
||||
rename races. However, for our hardened procfs handler, this in theory should
|
||||
prevent mount attacks from tricking us when doing magic-link readlinks (even
|
||||
when using the unsafe host `/proc` handle). Unfortunately `Reopen` is still
|
||||
potentially vulnerable to those kinds of somewhat-esoteric attacks.
|
||||
|
||||
Technically this [will only work on post-2.6.39 kernels][linux-readlinkat-emptypath]
|
||||
but it seems incredibly unlikely anyone is using `filepath-securejoin` on a
|
||||
pre-2011 kernel.
|
||||
|
||||
### Fixed ###
|
||||
- Several improvements were made to the errors returned by `Open(at)InRoot` and
|
||||
`MkdirAll` when dealing with invalid paths under the emulated (ie.
|
||||
non-`openat2`) implementation. Previously, some paths would return the wrong
|
||||
error (`ENOENT` when the last component was a non-directory), and other paths
|
||||
would be returned as though they were acceptable (trailing-slash components
|
||||
after a non-directory would be ignored by `Open(at)InRoot`).
|
||||
|
||||
These changes were done to match `openat2`'s behaviour and purely is a
|
||||
consistency fix (most users are going to be using `openat2` anyway).
|
||||
|
||||
[linux-readlinkat-emptypath]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=65cfc6722361570bfe255698d9cd4dccaf47570d
|
||||
|
||||
## [0.3.0] - 2024-07-11 ##
|
||||
|
||||
### Added ###
|
||||
- A new set of `*os.File`-based APIs have been added. These are adapted from
|
||||
[libpathrs][] and we strongly suggest using them if possible (as they provide
|
||||
far more protection against attacks than `SecureJoin`):
|
||||
|
||||
- `Open(at)InRoot` resolves a path inside a rootfs and returns an `*os.File`
|
||||
handle to the path. Note that the handle returned is an `O_PATH` handle,
|
||||
which cannot be used for reading or writing (as well as some other
|
||||
operations -- [see open(2) for more details][open.2])
|
||||
|
||||
- `Reopen` takes an `O_PATH` file handle and safely re-opens it to upgrade
|
||||
it to a regular handle. This can also be used with non-`O_PATH` handles,
|
||||
but `O_PATH` is the most obvious application.
|
||||
|
||||
- `MkdirAll` is an implementation of `os.MkdirAll` that is safe to use to
|
||||
create a directory tree within a rootfs.
|
||||
|
||||
As these are new APIs, they may change in the future. However, they should be
|
||||
safe to start migrating to as we have extensive tests ensuring they behave
|
||||
correctly and are safe against various races and other attacks.
|
||||
|
||||
[libpathrs]: https://github.com/openSUSE/libpathrs
|
||||
[open.2]: https://www.man7.org/linux/man-pages/man2/open.2.html
|
||||
|
||||
## [0.2.5] - 2024-05-03 ##
|
||||
|
||||
### Changed ###
|
||||
- Some minor changes were made to how lexical components (like `..` and `.`)
|
||||
are handled during path generation in `SecureJoin`. There is no behaviour
|
||||
change as a result of this fix (the resulting paths are the same).
|
||||
|
||||
### Fixed ###
|
||||
- The error returned when we hit a symlink loop now references the correct
|
||||
path. (#10)
|
||||
|
||||
## [0.2.4] - 2023-09-06 ##
|
||||
|
||||
### Security ###
|
||||
- This release fixes a potential security issue in filepath-securejoin when
|
||||
used on Windows ([GHSA-6xv5-86q9-7xr8][], which could be used to generate
|
||||
paths outside of the provided rootfs in certain cases), as well as improving
|
||||
the overall behaviour of filepath-securejoin when dealing with Windows paths
|
||||
that contain volume names. Thanks to Paulo Gomes for discovering and fixing
|
||||
these issues.
|
||||
|
||||
### Fixed ###
|
||||
- Switch to GitHub Actions for CI so we can test on Windows as well as Linux
|
||||
and MacOS.
|
||||
|
||||
[GHSA-6xv5-86q9-7xr8]: https://github.com/advisories/GHSA-6xv5-86q9-7xr8
|
||||
|
||||
## [0.2.3] - 2021-06-04 ##
|
||||
|
||||
### Changed ###
|
||||
- Switch to Go 1.13-style `%w` error wrapping, letting us drop the dependency
|
||||
on `github.com/pkg/errors`.
|
||||
|
||||
## [0.2.2] - 2018-09-05 ##
|
||||
|
||||
### Changed ###
|
||||
- Use `syscall.ELOOP` as the base error for symlink loops, rather than our own
|
||||
(internal) error. This allows callers to more easily use `errors.Is` to check
|
||||
for this case.
|
||||
|
||||
## [0.2.1] - 2018-09-05 ##
|
||||
|
||||
### Fixed ###
|
||||
- Use our own `IsNotExist` implementation, which lets us handle `ENOTDIR`
|
||||
properly within `SecureJoin`.
|
||||
|
||||
## [0.2.0] - 2017-07-19 ##
|
||||
|
||||
We now have 100% test coverage!
|
||||
|
||||
### Added ###
|
||||
- Add a `SecureJoinVFS` API that can be used for mocking (as we do in our new
|
||||
tests) or for implementing custom handling of lookup operations (such as for
|
||||
rootless containers, where work is necessary to access directories with weird
|
||||
modes because we don't have `CAP_DAC_READ_SEARCH` or `CAP_DAC_OVERRIDE`).
|
||||
|
||||
## 0.1.0 - 2017-07-19
|
||||
|
||||
This is our first release of `github.com/cyphar/filepath-securejoin`,
|
||||
containing a full implementation with a coverage of 93.5% (the only missing
|
||||
cases are the error cases, which are hard to mocktest at the moment).
|
||||
|
||||
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...HEAD
|
||||
[0.3.6]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.5...v0.3.6
|
||||
[0.3.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...v0.3.5
|
||||
[0.3.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4
|
||||
[0.3.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.2...v0.3.3
|
||||
[0.3.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.1...v0.3.2
|
||||
[0.3.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.5...v0.3.0
|
||||
[0.2.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.4...v0.2.5
|
||||
[0.2.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.3...v0.2.4
|
||||
[0.2.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.2...v0.2.3
|
||||
[0.2.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.1...v0.2.2
|
||||
[0.2.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.0...v0.2.1
|
||||
[0.2.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.1.0...v0.2.0
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
|
||||
Copyright (C) 2017 SUSE LLC. All rights reserved.
|
||||
Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
|
|
|
|||
|
|
@ -1,32 +1,26 @@
|
|||
## `filepath-securejoin` ##
|
||||
|
||||
[](https://pkg.go.dev/github.com/cyphar/filepath-securejoin)
|
||||
[](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml)
|
||||
|
||||
An implementation of `SecureJoin`, a [candidate for inclusion in the Go
|
||||
standard library][go#20126]. The purpose of this function is to be a "secure"
|
||||
alternative to `filepath.Join`, and in particular it provides certain
|
||||
guarantees that are not provided by `filepath.Join`.
|
||||
### Old API ###
|
||||
|
||||
> **NOTE**: This code is *only* safe if you are not at risk of other processes
|
||||
> modifying path components after you've used `SecureJoin`. If it is possible
|
||||
> for a malicious process to modify path components of the resolved path, then
|
||||
> you will be vulnerable to some fairly trivial TOCTOU race conditions. [There
|
||||
> are some Linux kernel patches I'm working on which might allow for a better
|
||||
> solution.][lwn-obeneath]
|
||||
>
|
||||
> In addition, with a slightly modified API it might be possible to use
|
||||
> `O_PATH` and verify that the opened path is actually the resolved one -- but
|
||||
> I have not done that yet. I might add it in the future as a helper function
|
||||
> to help users verify the path (we can't just return `/proc/self/fd/<foo>`
|
||||
> because that doesn't always work transparently for all users).
|
||||
This library was originally just an implementation of `SecureJoin` which was
|
||||
[intended to be included in the Go standard library][go#20126] as a safer
|
||||
`filepath.Join` that would restrict the path lookup to be inside a root
|
||||
directory.
|
||||
|
||||
This is the function prototype:
|
||||
The implementation was based on code that existed in several container
|
||||
runtimes. Unfortunately, this API is **fundamentally unsafe** against attackers
|
||||
that can modify path components after `SecureJoin` returns and before the
|
||||
caller uses the path, allowing for some fairly trivial TOCTOU attacks.
|
||||
|
||||
```go
|
||||
func SecureJoin(root, unsafePath string) (string, error)
|
||||
```
|
||||
`SecureJoin` (and `SecureJoinVFS`) are still provided by this library to
|
||||
support legacy users, but new users are strongly suggested to avoid using
|
||||
`SecureJoin` and instead use the [new api](#new-api) or switch to
|
||||
[libpathrs][libpathrs].
|
||||
|
||||
This library **guarantees** the following:
|
||||
With the above limitations in mind, this library guarantees the following:
|
||||
|
||||
* If no error is set, the resulting string **must** be a child path of
|
||||
`root` and will not contain any symlink path components (they will all be
|
||||
|
|
@ -47,7 +41,7 @@ This library **guarantees** the following:
|
|||
A (trivial) implementation of this function on GNU/Linux systems could be done
|
||||
with the following (note that this requires root privileges and is far more
|
||||
opaque than the implementation in this library, and also requires that
|
||||
`readlink` is inside the `root` path):
|
||||
`readlink` is inside the `root` path and is trustworthy):
|
||||
|
||||
```go
|
||||
package securejoin
|
||||
|
|
@ -70,9 +64,105 @@ func SecureJoin(root, unsafePath string) (string, error) {
|
|||
}
|
||||
```
|
||||
|
||||
[lwn-obeneath]: https://lwn.net/Articles/767547/
|
||||
[libpathrs]: https://github.com/openSUSE/libpathrs
|
||||
[go#20126]: https://github.com/golang/go/issues/20126
|
||||
|
||||
### New API ###
|
||||
|
||||
While we recommend users switch to [libpathrs][libpathrs] as soon as it has a
|
||||
stable release, some methods implemented by libpathrs have been ported to this
|
||||
library to ease the transition. These APIs are only supported on Linux.
|
||||
|
||||
These APIs are implemented such that `filepath-securejoin` will
|
||||
opportunistically use certain newer kernel APIs that make these operations far
|
||||
more secure. In particular:
|
||||
|
||||
* All of the lookup operations will use [`openat2`][openat2.2] on new enough
|
||||
kernels (Linux 5.6 or later) to restrict lookups through magic-links and
|
||||
bind-mounts (for certain operations) and to make use of `RESOLVE_IN_ROOT` to
|
||||
efficiently resolve symlinks within a rootfs.
|
||||
|
||||
* The APIs provide hardening against a malicious `/proc` mount to either detect
|
||||
or avoid being tricked by a `/proc` that is not legitimate. This is done
|
||||
using [`openat2`][openat2.2] for all users, and privileged users will also be
|
||||
further protected by using [`fsopen`][fsopen.2] and [`open_tree`][open_tree.2]
|
||||
(Linux 5.2 or later).
|
||||
|
||||
[openat2.2]: https://www.man7.org/linux/man-pages/man2/openat2.2.html
|
||||
[fsopen.2]: https://github.com/brauner/man-pages-md/blob/main/fsopen.md
|
||||
[open_tree.2]: https://github.com/brauner/man-pages-md/blob/main/open_tree.md
|
||||
|
||||
#### `OpenInRoot` ####
|
||||
|
||||
```go
|
||||
func OpenInRoot(root, unsafePath string) (*os.File, error)
|
||||
func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error)
|
||||
func Reopen(handle *os.File, flags int) (*os.File, error)
|
||||
```
|
||||
|
||||
`OpenInRoot` is a much safer version of
|
||||
|
||||
```go
|
||||
path, err := securejoin.SecureJoin(root, unsafePath)
|
||||
file, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC)
|
||||
```
|
||||
|
||||
that protects against various race attacks that could lead to serious security
|
||||
issues, depending on the application. Note that the returned `*os.File` is an
|
||||
`O_PATH` file descriptor, which is quite restricted. Callers will probably need
|
||||
to use `Reopen` to get a more usable handle (this split is done to provide
|
||||
useful features like PTY spawning and to avoid users accidentally opening bad
|
||||
inodes that could cause a DoS).
|
||||
|
||||
Callers need to be careful in how they use the returned `*os.File`. Usually it
|
||||
is only safe to operate on the handle directly, and it is very easy to create a
|
||||
security issue. [libpathrs][libpathrs] provides far more helpers to make using
|
||||
these handles safer -- there is currently no plan to port them to
|
||||
`filepath-securejoin`.
|
||||
|
||||
`OpenatInRoot` is like `OpenInRoot` except that the root is provided using an
|
||||
`*os.File`. This allows you to ensure that multiple `OpenatInRoot` (or
|
||||
`MkdirAllHandle`) calls are operating on the same rootfs.
|
||||
|
||||
> **NOTE**: Unlike `SecureJoin`, `OpenInRoot` will error out as soon as it hits
|
||||
> a dangling symlink or non-existent path. This is in contrast to `SecureJoin`
|
||||
> which treated non-existent components as though they were real directories,
|
||||
> and would allow for partial resolution of dangling symlinks. These behaviours
|
||||
> are at odds with how Linux treats non-existent paths and dangling symlinks,
|
||||
> and so these are no longer allowed.
|
||||
|
||||
#### `MkdirAll` ####
|
||||
|
||||
```go
|
||||
func MkdirAll(root, unsafePath string, mode int) error
|
||||
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (*os.File, error)
|
||||
```
|
||||
|
||||
`MkdirAll` is a much safer version of
|
||||
|
||||
```go
|
||||
path, err := securejoin.SecureJoin(root, unsafePath)
|
||||
err = os.MkdirAll(path, mode)
|
||||
```
|
||||
|
||||
that protects against the same kinds of races that `OpenInRoot` protects
|
||||
against.
|
||||
|
||||
`MkdirAllHandle` is like `MkdirAll` except that the root is provided using an
|
||||
`*os.File` (the reason for this is the same as with `OpenatInRoot`) and an
|
||||
`*os.File` of the final created directory is returned (this directory is
|
||||
guaranteed to be effectively identical to the directory created by
|
||||
`MkdirAllHandle`, which is not possible to ensure by just using `OpenatInRoot`
|
||||
after `MkdirAll`).
|
||||
|
||||
> **NOTE**: Unlike `SecureJoin`, `MkdirAll` will error out as soon as it hits
|
||||
> a dangling symlink or non-existent path. This is in contrast to `SecureJoin`
|
||||
> which treated non-existent components as though they were real directories,
|
||||
> and would allow for partial resolution of dangling symlinks. These behaviours
|
||||
> are at odds with how Linux treats non-existent paths and dangling symlinks,
|
||||
> and so these are no longer allowed. This means that `MkdirAll` will not
|
||||
> create non-existent directories referenced by a dangling symlink.
|
||||
|
||||
### License ###
|
||||
|
||||
The license of this project is the same as Go, which is a BSD 3-clause license
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0.2.4
|
||||
0.3.6
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
|
||||
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package securejoin implements a set of helpers to make it easier to write Go
|
||||
// code that is safe against symlink-related escape attacks. The primary idea
|
||||
// is to let you resolve a path within a rootfs directory as if the rootfs was
|
||||
// a chroot.
|
||||
//
|
||||
// securejoin has two APIs, a "legacy" API and a "modern" API.
|
||||
//
|
||||
// The legacy API is [SecureJoin] and [SecureJoinVFS]. These methods are
|
||||
// **not** safe against race conditions where an attacker changes the
|
||||
// filesystem after (or during) the [SecureJoin] operation.
|
||||
//
|
||||
// The new API is made up of [OpenInRoot] and [MkdirAll] (and derived
|
||||
// functions). These are safe against racing attackers and have several other
|
||||
// protections that are not provided by the legacy API. There are many more
|
||||
// operations that most programs expect to be able to do safely, but we do not
|
||||
// provide explicit support for them because we want to encourage users to
|
||||
// switch to [libpathrs](https://github.com/openSUSE/libpathrs) which is a
|
||||
// cross-language next-generation library that is entirely designed around
|
||||
// operating on paths safely.
|
||||
//
|
||||
// securejoin has been used by several container runtimes (Docker, runc,
|
||||
// Kubernetes, etc) for quite a few years as a de-facto standard for operating
|
||||
// on container filesystem paths "safely". However, most users still use the
|
||||
// legacy API which is unsafe against various attacks (there is a fairly long
|
||||
// history of CVEs in dependent as a result). Users should switch to the modern
|
||||
// API as soon as possible (or even better, switch to libpathrs).
|
||||
//
|
||||
// This project was initially intended to be included in the Go standard
|
||||
// library, but [it was rejected](https://go.dev/issue/20126). There is now a
|
||||
// [new Go proposal](https://go.dev/issue/67002) for a safe path resolution API
|
||||
// that shares some of the goals of filepath-securejoin. However, that design
|
||||
// is intended to work like `openat2(RESOLVE_BENEATH)` which does not fit the
|
||||
// usecase of container runtimes and most system tools.
|
||||
package securejoin
|
||||
18
vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go
generated
vendored
Normal file
18
vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//go:build linux && go1.20
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
|
||||
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
|
||||
// is only guaranteed to give you baseErr.
|
||||
func wrapBaseError(baseErr, extraErr error) error {
|
||||
return fmt.Errorf("%w: %w", extraErr, baseErr)
|
||||
}
|
||||
38
vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go
generated
vendored
Normal file
38
vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//go:build linux && !go1.20
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
inner error
|
||||
isError error
|
||||
}
|
||||
|
||||
func (err wrappedError) Is(target error) bool {
|
||||
return err.isError == target
|
||||
}
|
||||
|
||||
func (err wrappedError) Unwrap() error {
|
||||
return err.inner
|
||||
}
|
||||
|
||||
func (err wrappedError) Error() string {
|
||||
return fmt.Sprintf("%v: %v", err.isError, err.inner)
|
||||
}
|
||||
|
||||
// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
|
||||
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
|
||||
// is only guaranteed to give you baseErr.
|
||||
func wrapBaseError(baseErr, extraErr error) error {
|
||||
return wrappedError{
|
||||
inner: baseErr,
|
||||
isError: extraErr,
|
||||
}
|
||||
}
|
||||
32
vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go
generated
vendored
Normal file
32
vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
//go:build linux && go1.21
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func slices_DeleteFunc[S ~[]E, E any](slice S, delFn func(E) bool) S {
|
||||
return slices.DeleteFunc(slice, delFn)
|
||||
}
|
||||
|
||||
func slices_Contains[S ~[]E, E comparable](slice S, val E) bool {
|
||||
return slices.Contains(slice, val)
|
||||
}
|
||||
|
||||
func slices_Clone[S ~[]E, E any](slice S) S {
|
||||
return slices.Clone(slice)
|
||||
}
|
||||
|
||||
func sync_OnceValue[T any](f func() T) func() T {
|
||||
return sync.OnceValue(f)
|
||||
}
|
||||
|
||||
func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
||||
return sync.OnceValues(f)
|
||||
}
|
||||
124
vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go
generated
vendored
Normal file
124
vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
//go:build linux && !go1.21
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// These are very minimal implementations of functions that appear in Go 1.21's
|
||||
// stdlib, included so that we can build on older Go versions. Most are
|
||||
// borrowed directly from the stdlib, and a few are modified to be "obviously
|
||||
// correct" without needing to copy too many other helpers.
|
||||
|
||||
// clearSlice is equivalent to the builtin clear from Go 1.21.
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func clearSlice[S ~[]E, E any](slice S) {
|
||||
var zero E
|
||||
for i := range slice {
|
||||
slice[i] = zero
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func slices_IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
|
||||
for i := range s {
|
||||
if f(s[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func slices_DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
|
||||
i := slices_IndexFunc(s, del)
|
||||
if i == -1 {
|
||||
return s
|
||||
}
|
||||
// Don't start copying elements until we find one to delete.
|
||||
for j := i + 1; j < len(s); j++ {
|
||||
if v := s[j]; !del(v) {
|
||||
s[i] = v
|
||||
i++
|
||||
}
|
||||
}
|
||||
clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// Similar to the stdlib slices.Contains, except that we don't have
|
||||
// slices.Index so we need to use slices.IndexFunc for this non-Func helper.
|
||||
func slices_Contains[S ~[]E, E comparable](s S, v E) bool {
|
||||
return slices_IndexFunc(s, func(e E) bool { return e == v }) >= 0
|
||||
}
|
||||
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func slices_Clone[S ~[]E, E any](s S) S {
|
||||
// Preserve nil in case it matters.
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return append(S([]E{}), s...)
|
||||
}
|
||||
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func sync_OnceValue[T any](f func() T) func() T {
|
||||
var (
|
||||
once sync.Once
|
||||
valid bool
|
||||
p any
|
||||
result T
|
||||
)
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
result = f()
|
||||
f = nil
|
||||
valid = true
|
||||
}
|
||||
return func() T {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
||||
var (
|
||||
once sync.Once
|
||||
valid bool
|
||||
p any
|
||||
r1 T1
|
||||
r2 T2
|
||||
)
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
r1, r2 = f()
|
||||
f = nil
|
||||
valid = true
|
||||
}
|
||||
return func() (T1, T2) {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
return r1, r2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,11 @@
|
|||
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
|
||||
// Copyright (C) 2017 SUSE LLC. All rights reserved.
|
||||
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package securejoin is an implementation of the hopefully-soon-to-be-included
|
||||
// SecureJoin helper that is meant to be part of the "path/filepath" package.
|
||||
// The purpose of this project is to provide a PoC implementation to make the
|
||||
// SecureJoin proposal (https://github.com/golang/go/issues/20126) more
|
||||
// tangible.
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -19,26 +13,34 @@ import (
|
|||
"syscall"
|
||||
)
|
||||
|
||||
const maxSymlinkLimit = 255
|
||||
|
||||
// IsNotExist tells you if err is an error that implies that either the path
|
||||
// accessed does not exist (or path components don't exist). This is
|
||||
// effectively a more broad version of os.IsNotExist.
|
||||
// effectively a more broad version of [os.IsNotExist].
|
||||
func IsNotExist(err error) bool {
|
||||
// Check that it's not actually an ENOTDIR, which in some cases is a more
|
||||
// convoluted case of ENOENT (usually involving weird paths).
|
||||
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT)
|
||||
}
|
||||
|
||||
// SecureJoinVFS joins the two given path components (similar to Join) except
|
||||
// SecureJoinVFS joins the two given path components (similar to [filepath.Join]) except
|
||||
// that the returned path is guaranteed to be scoped inside the provided root
|
||||
// path (when evaluated). Any symbolic links in the path are evaluated with the
|
||||
// given root treated as the root of the filesystem, similar to a chroot. The
|
||||
// filesystem state is evaluated through the given VFS interface (if nil, the
|
||||
// standard os.* family of functions are used).
|
||||
// filesystem state is evaluated through the given [VFS] interface (if nil, the
|
||||
// standard [os].* family of functions are used).
|
||||
//
|
||||
// Note that the guarantees provided by this function only apply if the path
|
||||
// components in the returned string are not modified (in other words are not
|
||||
// replaced with symlinks on the filesystem) after this function has returned.
|
||||
// Such a symlink race is necessarily out-of-scope of SecureJoin.
|
||||
// Such a symlink race is necessarily out-of-scope of SecureJoinVFS.
|
||||
//
|
||||
// NOTE: Due to the above limitation, Linux users are strongly encouraged to
|
||||
// use [OpenInRoot] instead, which does safely protect against these kinds of
|
||||
// attacks. There is no way to solve this problem with SecureJoinVFS because
|
||||
// the API is fundamentally wrong (you cannot return a "safe" path string and
|
||||
// guarantee it won't be modified afterwards).
|
||||
//
|
||||
// Volume names in unsafePath are always discarded, regardless if they are
|
||||
// provided via direct input or when evaluating symlinks. Therefore:
|
||||
|
|
@ -51,75 +53,73 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
|
|||
}
|
||||
|
||||
unsafePath = filepath.FromSlash(unsafePath)
|
||||
var path bytes.Buffer
|
||||
n := 0
|
||||
for unsafePath != "" {
|
||||
if n > 255 {
|
||||
return "", &os.PathError{Op: "SecureJoin", Path: root + string(filepath.Separator) + unsafePath, Err: syscall.ELOOP}
|
||||
var (
|
||||
currentPath string
|
||||
remainingPath = unsafePath
|
||||
linksWalked int
|
||||
)
|
||||
for remainingPath != "" {
|
||||
if v := filepath.VolumeName(remainingPath); v != "" {
|
||||
remainingPath = remainingPath[len(v):]
|
||||
}
|
||||
|
||||
if v := filepath.VolumeName(unsafePath); v != "" {
|
||||
unsafePath = unsafePath[len(v):]
|
||||
}
|
||||
|
||||
// Next path component, p.
|
||||
i := strings.IndexRune(unsafePath, filepath.Separator)
|
||||
var p string
|
||||
if i == -1 {
|
||||
p, unsafePath = unsafePath, ""
|
||||
// Get the next path component.
|
||||
var part string
|
||||
if i := strings.IndexRune(remainingPath, filepath.Separator); i == -1 {
|
||||
part, remainingPath = remainingPath, ""
|
||||
} else {
|
||||
p, unsafePath = unsafePath[:i], unsafePath[i+1:]
|
||||
part, remainingPath = remainingPath[:i], remainingPath[i+1:]
|
||||
}
|
||||
|
||||
// Create a cleaned path, using the lexical semantics of /../a, to
|
||||
// create a "scoped" path component which can safely be joined to fullP
|
||||
// for evaluation. At this point, path.String() doesn't contain any
|
||||
// symlink components.
|
||||
cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p)
|
||||
if cleanP == string(filepath.Separator) {
|
||||
path.Reset()
|
||||
// Apply the component lexically to the path we are building.
|
||||
// currentPath does not contain any symlinks, and we are lexically
|
||||
// dealing with a single component, so it's okay to do a filepath.Clean
|
||||
// here.
|
||||
nextPath := filepath.Join(string(filepath.Separator), currentPath, part)
|
||||
if nextPath == string(filepath.Separator) {
|
||||
currentPath = ""
|
||||
continue
|
||||
}
|
||||
fullP := filepath.Clean(root + cleanP)
|
||||
fullPath := root + string(filepath.Separator) + nextPath
|
||||
|
||||
// Figure out whether the path is a symlink.
|
||||
fi, err := vfs.Lstat(fullP)
|
||||
fi, err := vfs.Lstat(fullPath)
|
||||
if err != nil && !IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
// Treat non-existent path components the same as non-symlinks (we
|
||||
// can't do any better here).
|
||||
if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 {
|
||||
path.WriteString(p)
|
||||
path.WriteRune(filepath.Separator)
|
||||
currentPath = nextPath
|
||||
continue
|
||||
}
|
||||
|
||||
// Only increment when we actually dereference a link.
|
||||
n++
|
||||
// It's a symlink, so get its contents and expand it by prepending it
|
||||
// to the yet-unparsed path.
|
||||
linksWalked++
|
||||
if linksWalked > maxSymlinkLimit {
|
||||
return "", &os.PathError{Op: "SecureJoin", Path: root + string(filepath.Separator) + unsafePath, Err: syscall.ELOOP}
|
||||
}
|
||||
|
||||
// It's a symlink, expand it by prepending it to the yet-unparsed path.
|
||||
dest, err := vfs.Readlink(fullP)
|
||||
dest, err := vfs.Readlink(fullPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
remainingPath = dest + string(filepath.Separator) + remainingPath
|
||||
// Absolute symlinks reset any work we've already done.
|
||||
if filepath.IsAbs(dest) {
|
||||
path.Reset()
|
||||
currentPath = ""
|
||||
}
|
||||
unsafePath = dest + string(filepath.Separator) + unsafePath
|
||||
}
|
||||
|
||||
// We have to clean path.String() here because it may contain '..'
|
||||
// components that are entirely lexical, but would be misleading otherwise.
|
||||
// And finally do a final clean to ensure that root is also lexically
|
||||
// clean.
|
||||
fullP := filepath.Clean(string(filepath.Separator) + path.String())
|
||||
return filepath.Clean(root + fullP), nil
|
||||
// There should be no lexical components like ".." left in the path here,
|
||||
// but for safety clean up the path before joining it to the root.
|
||||
finalPath := filepath.Join(string(filepath.Separator), currentPath)
|
||||
return filepath.Join(root, finalPath), nil
|
||||
}
|
||||
|
||||
// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library
|
||||
// of functions as the VFS. If in doubt, use this function over SecureJoinVFS.
|
||||
// SecureJoin is a wrapper around [SecureJoinVFS] that just uses the [os].* library
|
||||
// of functions as the [VFS]. If in doubt, use this function over [SecureJoinVFS].
|
||||
func SecureJoin(root, unsafePath string) (string, error) {
|
||||
return SecureJoinVFS(root, unsafePath, nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,388 @@
|
|||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type symlinkStackEntry struct {
|
||||
// (dir, remainingPath) is what we would've returned if the link didn't
|
||||
// exist. This matches what openat2(RESOLVE_IN_ROOT) would return in
|
||||
// this case.
|
||||
dir *os.File
|
||||
remainingPath string
|
||||
// linkUnwalked is the remaining path components from the original
|
||||
// Readlink which we have yet to walk. When this slice is empty, we
|
||||
// drop the link from the stack.
|
||||
linkUnwalked []string
|
||||
}
|
||||
|
||||
func (se symlinkStackEntry) String() string {
|
||||
return fmt.Sprintf("<%s>/%s [->%s]", se.dir.Name(), se.remainingPath, strings.Join(se.linkUnwalked, "/"))
|
||||
}
|
||||
|
||||
func (se symlinkStackEntry) Close() {
|
||||
_ = se.dir.Close()
|
||||
}
|
||||
|
||||
type symlinkStack []*symlinkStackEntry
|
||||
|
||||
func (s *symlinkStack) IsEmpty() bool {
|
||||
return s == nil || len(*s) == 0
|
||||
}
|
||||
|
||||
func (s *symlinkStack) Close() {
|
||||
if s != nil {
|
||||
for _, link := range *s {
|
||||
link.Close()
|
||||
}
|
||||
// TODO: Switch to clear once we switch to Go 1.21.
|
||||
*s = nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
errEmptyStack = errors.New("[internal] stack is empty")
|
||||
errBrokenSymlinkStack = errors.New("[internal error] broken symlink stack")
|
||||
)
|
||||
|
||||
func (s *symlinkStack) popPart(part string) error {
|
||||
if s == nil || s.IsEmpty() {
|
||||
// If there is nothing in the symlink stack, then the part was from the
|
||||
// real path provided by the user, and this is a no-op.
|
||||
return errEmptyStack
|
||||
}
|
||||
if part == "." {
|
||||
// "." components are no-ops -- we drop them when doing SwapLink.
|
||||
return nil
|
||||
}
|
||||
|
||||
tailEntry := (*s)[len(*s)-1]
|
||||
|
||||
// Double-check that we are popping the component we expect.
|
||||
if len(tailEntry.linkUnwalked) == 0 {
|
||||
return fmt.Errorf("%w: trying to pop component %q of empty stack entry %s", errBrokenSymlinkStack, part, tailEntry)
|
||||
}
|
||||
headPart := tailEntry.linkUnwalked[0]
|
||||
if headPart != part {
|
||||
return fmt.Errorf("%w: trying to pop component %q but the last stack entry is %s (%q)", errBrokenSymlinkStack, part, tailEntry, headPart)
|
||||
}
|
||||
|
||||
// Drop the component, but keep the entry around in case we are dealing
|
||||
// with a "tail-chained" symlink.
|
||||
tailEntry.linkUnwalked = tailEntry.linkUnwalked[1:]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *symlinkStack) PopPart(part string) error {
|
||||
if err := s.popPart(part); err != nil {
|
||||
if errors.Is(err, errEmptyStack) {
|
||||
// Skip empty stacks.
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Clean up any of the trailing stack entries that are empty.
|
||||
for lastGood := len(*s) - 1; lastGood >= 0; lastGood-- {
|
||||
entry := (*s)[lastGood]
|
||||
if len(entry.linkUnwalked) > 0 {
|
||||
break
|
||||
}
|
||||
entry.Close()
|
||||
(*s) = (*s)[:lastGood]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *symlinkStack) push(dir *os.File, remainingPath, linkTarget string) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
// Split the link target and clean up any "" parts.
|
||||
linkTargetParts := slices_DeleteFunc(
|
||||
strings.Split(linkTarget, "/"),
|
||||
func(part string) bool { return part == "" || part == "." })
|
||||
|
||||
// Copy the directory so the caller doesn't close our copy.
|
||||
dirCopy, err := dupFile(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add to the stack.
|
||||
*s = append(*s, &symlinkStackEntry{
|
||||
dir: dirCopy,
|
||||
remainingPath: remainingPath,
|
||||
linkUnwalked: linkTargetParts,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *symlinkStack) SwapLink(linkPart string, dir *os.File, remainingPath, linkTarget string) error {
|
||||
// If we are currently inside a symlink resolution, remove the symlink
|
||||
// component from the last symlink entry, but don't remove the entry even
|
||||
// if it's empty. If we are a "tail-chained" symlink (a trailing symlink we
|
||||
// hit during a symlink resolution) we need to keep the old symlink until
|
||||
// we finish the resolution.
|
||||
if err := s.popPart(linkPart); err != nil {
|
||||
if !errors.Is(err, errEmptyStack) {
|
||||
return err
|
||||
}
|
||||
// Push the component regardless of whether the stack was empty.
|
||||
}
|
||||
return s.push(dir, remainingPath, linkTarget)
|
||||
}
|
||||
|
||||
func (s *symlinkStack) PopTopSymlink() (*os.File, string, bool) {
|
||||
if s == nil || s.IsEmpty() {
|
||||
return nil, "", false
|
||||
}
|
||||
tailEntry := (*s)[0]
|
||||
*s = (*s)[1:]
|
||||
return tailEntry.dir, tailEntry.remainingPath, true
|
||||
}
|
||||
|
||||
// partialLookupInRoot tries to lookup as much of the request path as possible
|
||||
// within the provided root (a-la RESOLVE_IN_ROOT) and opens the final existing
|
||||
// component of the requested path, returning a file handle to the final
|
||||
// existing component and a string containing the remaining path components.
|
||||
func partialLookupInRoot(root *os.File, unsafePath string) (*os.File, string, error) {
|
||||
return lookupInRoot(root, unsafePath, true)
|
||||
}
|
||||
|
||||
func completeLookupInRoot(root *os.File, unsafePath string) (*os.File, error) {
|
||||
handle, remainingPath, err := lookupInRoot(root, unsafePath, false)
|
||||
if remainingPath != "" && err == nil {
|
||||
// should never happen
|
||||
err = fmt.Errorf("[bug] non-empty remaining path when doing a non-partial lookup: %q", remainingPath)
|
||||
}
|
||||
// lookupInRoot(partial=false) will always close the handle if an error is
|
||||
// returned, so no need to double-check here.
|
||||
return handle, err
|
||||
}
|
||||
|
||||
func lookupInRoot(root *os.File, unsafePath string, partial bool) (Handle *os.File, _ string, _ error) {
|
||||
unsafePath = filepath.ToSlash(unsafePath) // noop
|
||||
|
||||
// This is very similar to SecureJoin, except that we operate on the
|
||||
// components using file descriptors. We then return the last component we
|
||||
// managed open, along with the remaining path components not opened.
|
||||
|
||||
// Try to use openat2 if possible.
|
||||
if hasOpenat2() {
|
||||
return lookupOpenat2(root, unsafePath, partial)
|
||||
}
|
||||
|
||||
// Get the "actual" root path from /proc/self/fd. This is necessary if the
|
||||
// root is some magic-link like /proc/$pid/root, in which case we want to
|
||||
// make sure when we do checkProcSelfFdPath that we are using the correct
|
||||
// root path.
|
||||
logicalRootPath, err := procSelfFdReadlink(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("get real root path: %w", err)
|
||||
}
|
||||
|
||||
currentDir, err := dupFile(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
// If a handle is not returned, close the internal handle.
|
||||
if Handle == nil {
|
||||
_ = currentDir.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// symlinkStack is used to emulate how openat2(RESOLVE_IN_ROOT) treats
|
||||
// dangling symlinks. If we hit a non-existent path while resolving a
|
||||
// symlink, we need to return the (dir, remainingPath) that we had when we
|
||||
// hit the symlink (treating the symlink as though it were a regular file).
|
||||
// The set of (dir, remainingPath) sets is stored within the symlinkStack
|
||||
// and we add and remove parts when we hit symlink and non-symlink
|
||||
// components respectively. We need a stack because of recursive symlinks
|
||||
// (symlinks that contain symlink components in their target).
|
||||
//
|
||||
// Note that the stack is ONLY used for book-keeping. All of the actual
|
||||
// path walking logic is still based on currentPath/remainingPath and
|
||||
// currentDir (as in SecureJoin).
|
||||
var symStack *symlinkStack
|
||||
if partial {
|
||||
symStack = new(symlinkStack)
|
||||
defer symStack.Close()
|
||||
}
|
||||
|
||||
var (
|
||||
linksWalked int
|
||||
currentPath string
|
||||
remainingPath = unsafePath
|
||||
)
|
||||
for remainingPath != "" {
|
||||
// Save the current remaining path so if the part is not real we can
|
||||
// return the path including the component.
|
||||
oldRemainingPath := remainingPath
|
||||
|
||||
// Get the next path component.
|
||||
var part string
|
||||
if i := strings.IndexByte(remainingPath, '/'); i == -1 {
|
||||
part, remainingPath = remainingPath, ""
|
||||
} else {
|
||||
part, remainingPath = remainingPath[:i], remainingPath[i+1:]
|
||||
}
|
||||
// If we hit an empty component, we need to treat it as though it is
|
||||
// "." so that trailing "/" and "//" components on a non-directory
|
||||
// correctly return the right error code.
|
||||
if part == "" {
|
||||
part = "."
|
||||
}
|
||||
|
||||
// Apply the component lexically to the path we are building.
|
||||
// currentPath does not contain any symlinks, and we are lexically
|
||||
// dealing with a single component, so it's okay to do a filepath.Clean
|
||||
// here.
|
||||
nextPath := path.Join("/", currentPath, part)
|
||||
// If we logically hit the root, just clone the root rather than
|
||||
// opening the part and doing all of the other checks.
|
||||
if nextPath == "/" {
|
||||
if err := symStack.PopPart(part); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into root with part %q failed: %w", part, err)
|
||||
}
|
||||
// Jump to root.
|
||||
rootClone, err := dupFile(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = rootClone
|
||||
currentPath = nextPath
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to open the next component.
|
||||
nextDir, err := openatFile(currentDir, part, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
|
||||
switch {
|
||||
case err == nil:
|
||||
st, err := nextDir.Stat()
|
||||
if err != nil {
|
||||
_ = nextDir.Close()
|
||||
return nil, "", fmt.Errorf("stat component %q: %w", part, err)
|
||||
}
|
||||
|
||||
switch st.Mode() & os.ModeType {
|
||||
case os.ModeSymlink:
|
||||
// readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See
|
||||
// Linux commit 65cfc6722361 ("readlinkat(), fchownat() and
|
||||
// fstatat() with empty relative pathnames").
|
||||
linkDest, err := readlinkatFile(nextDir, "")
|
||||
// We don't need the handle anymore.
|
||||
_ = nextDir.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
linksWalked++
|
||||
if linksWalked > maxSymlinkLimit {
|
||||
return nil, "", &os.PathError{Op: "securejoin.lookupInRoot", Path: logicalRootPath + "/" + unsafePath, Err: unix.ELOOP}
|
||||
}
|
||||
|
||||
// Swap out the symlink's component for the link entry itself.
|
||||
if err := symStack.SwapLink(part, currentDir, oldRemainingPath, linkDest); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into symlink %q failed: push symlink: %w", part, err)
|
||||
}
|
||||
|
||||
// Update our logical remaining path.
|
||||
remainingPath = linkDest + "/" + remainingPath
|
||||
// Absolute symlinks reset any work we've already done.
|
||||
if path.IsAbs(linkDest) {
|
||||
// Jump to root.
|
||||
rootClone, err := dupFile(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = rootClone
|
||||
currentPath = "/"
|
||||
}
|
||||
|
||||
default:
|
||||
// If we are dealing with a directory, simply walk into it.
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
currentPath = nextPath
|
||||
|
||||
// The part was real, so drop it from the symlink stack.
|
||||
if err := symStack.PopPart(part); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into directory %q failed: %w", part, err)
|
||||
}
|
||||
|
||||
// If we are operating on a .., make sure we haven't escaped.
|
||||
// We only have to check for ".." here because walking down
|
||||
// into a regular component component cannot cause you to
|
||||
// escape. This mirrors the logic in RESOLVE_IN_ROOT, except we
|
||||
// have to check every ".." rather than only checking after a
|
||||
// rename or mount on the system.
|
||||
if part == ".." {
|
||||
// Make sure the root hasn't moved.
|
||||
if err := checkProcSelfFdPath(logicalRootPath, root); err != nil {
|
||||
return nil, "", fmt.Errorf("root path moved during lookup: %w", err)
|
||||
}
|
||||
// Make sure the path is what we expect.
|
||||
fullPath := logicalRootPath + nextPath
|
||||
if err := checkProcSelfFdPath(fullPath, currentDir); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into %q had unexpected result: %w", part, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if !partial {
|
||||
return nil, "", err
|
||||
}
|
||||
// If there are any remaining components in the symlink stack, we
|
||||
// are still within a symlink resolution and thus we hit a dangling
|
||||
// symlink. So pretend that the first symlink in the stack we hit
|
||||
// was an ENOENT (to match openat2).
|
||||
if oldDir, remainingPath, ok := symStack.PopTopSymlink(); ok {
|
||||
_ = currentDir.Close()
|
||||
return oldDir, remainingPath, err
|
||||
}
|
||||
// We have hit a final component that doesn't exist, so we have our
|
||||
// partial open result. Note that we have to use the OLD remaining
|
||||
// path, since the lookup failed.
|
||||
return currentDir, oldRemainingPath, err
|
||||
}
|
||||
}
|
||||
|
||||
// If the unsafePath had a trailing slash, we need to make sure we try to
|
||||
// do a relative "." open so that we will correctly return an error when
|
||||
// the final component is a non-directory (to match openat2). In the
|
||||
// context of openat2, a trailing slash and a trailing "/." are completely
|
||||
// equivalent.
|
||||
if strings.HasSuffix(unsafePath, "/") {
|
||||
nextDir, err := openatFile(currentDir, ".", unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
if !partial {
|
||||
_ = currentDir.Close()
|
||||
currentDir = nil
|
||||
}
|
||||
return currentDir, "", err
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
}
|
||||
|
||||
// All of the components existed!
|
||||
return currentDir, "", nil
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidMode = errors.New("invalid permission mode")
|
||||
errPossibleAttack = errors.New("possible attack detected")
|
||||
)
|
||||
|
||||
// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use
|
||||
// in two respects:
|
||||
//
|
||||
// - The caller provides the root directory as an *[os.File] (preferably O_PATH)
|
||||
// handle. This means that the caller can be sure which root directory is
|
||||
// being used. Note that this can be emulated by using /proc/self/fd/... as
|
||||
// the root path with [os.MkdirAll].
|
||||
//
|
||||
// - Once all of the directories have been created, an *[os.File] O_PATH handle
|
||||
// to the directory at unsafePath is returned to the caller. This is done in
|
||||
// an effectively-race-free way (an attacker would only be able to swap the
|
||||
// final directory component), which is not possible to emulate with
|
||||
// [MkdirAll].
|
||||
//
|
||||
// In addition, the returned handle is obtained far more efficiently than doing
|
||||
// a brand new lookup of unsafePath (such as with [SecureJoin] or openat2) after
|
||||
// doing [MkdirAll]. If you intend to open the directory after creating it, you
|
||||
// should use MkdirAllHandle.
|
||||
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err error) {
|
||||
// Make sure there are no os.FileMode bits set.
|
||||
if mode&^0o7777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir 0o%.3o", errInvalidMode, mode)
|
||||
}
|
||||
// On Linux, mkdirat(2) (and os.Mkdir) silently ignore the suid and sgid
|
||||
// bits. We could also silently ignore them but since we have very few
|
||||
// users it seems more prudent to return an error so users notice that
|
||||
// these bits will not be set.
|
||||
if mode&^0o1777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir 0o%.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
|
||||
}
|
||||
|
||||
// Try to open as much of the path as possible.
|
||||
currentDir, remainingPath, err := partialLookupInRoot(root, unsafePath)
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = currentDir.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil && !errors.Is(err, unix.ENOENT) {
|
||||
return nil, fmt.Errorf("find existing subpath of %q: %w", unsafePath, err)
|
||||
}
|
||||
|
||||
// If there is an attacker deleting directories as we walk into them,
|
||||
// detect this proactively. Note this is guaranteed to detect if the
|
||||
// attacker deleted any part of the tree up to currentDir.
|
||||
//
|
||||
// Once we walk into a dead directory, partialLookupInRoot would not be
|
||||
// able to walk further down the tree (directories must be empty before
|
||||
// they are deleted), and if the attacker has removed the entire tree we
|
||||
// can be sure that anything that was originally inside a dead directory
|
||||
// must also be deleted and thus is a dead directory in its own right.
|
||||
//
|
||||
// This is mostly a quality-of-life check, because mkdir will simply fail
|
||||
// later if the attacker deletes the tree after this check.
|
||||
if err := isDeadInode(currentDir); err != nil {
|
||||
return nil, fmt.Errorf("finding existing subpath of %q: %w", unsafePath, err)
|
||||
}
|
||||
|
||||
// Re-open the path to match the O_DIRECTORY reopen loop later (so that we
|
||||
// always return a non-O_PATH handle). We also check that we actually got a
|
||||
// directory.
|
||||
if reopenDir, err := Reopen(currentDir, unix.O_DIRECTORY|unix.O_CLOEXEC); errors.Is(err, unix.ENOTDIR) {
|
||||
return nil, fmt.Errorf("cannot create subdirectories in %q: %w", currentDir.Name(), unix.ENOTDIR)
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("re-opening handle to %q: %w", currentDir.Name(), err)
|
||||
} else {
|
||||
_ = currentDir.Close()
|
||||
currentDir = reopenDir
|
||||
}
|
||||
|
||||
remainingParts := strings.Split(remainingPath, string(filepath.Separator))
|
||||
if slices_Contains(remainingParts, "..") {
|
||||
// The path contained ".." components after the end of the "real"
|
||||
// components. We could try to safely resolve ".." here but that would
|
||||
// add a bunch of extra logic for something that it's not clear even
|
||||
// needs to be supported. So just return an error.
|
||||
//
|
||||
// If we do filepath.Clean(remainingPath) then we end up with the
|
||||
// problem that ".." can erase a trailing dangling symlink and produce
|
||||
// a path that doesn't quite match what the user asked for.
|
||||
return nil, fmt.Errorf("%w: yet-to-be-created path %q contains '..' components", unix.ENOENT, remainingPath)
|
||||
}
|
||||
|
||||
// Make sure the mode doesn't have any type bits.
|
||||
mode &^= unix.S_IFMT
|
||||
|
||||
// Create the remaining components.
|
||||
for _, part := range remainingParts {
|
||||
switch part {
|
||||
case "", ".":
|
||||
// Skip over no-op paths.
|
||||
continue
|
||||
}
|
||||
|
||||
// NOTE: mkdir(2) will not follow trailing symlinks, so we can safely
|
||||
// create the final component without worrying about symlink-exchange
|
||||
// attacks.
|
||||
//
|
||||
// If we get -EEXIST, it's possible that another program created the
|
||||
// directory at the same time as us. In that case, just continue on as
|
||||
// if we created it (if the created inode is not a directory, the
|
||||
// following open call will fail).
|
||||
if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
|
||||
// Make the error a bit nicer if the directory is dead.
|
||||
if deadErr := isDeadInode(currentDir); deadErr != nil {
|
||||
// TODO: Once we bump the minimum Go version to 1.20, we can use
|
||||
// multiple %w verbs for this wrapping. For now we need to use a
|
||||
// compatibility shim for older Go versions.
|
||||
//err = fmt.Errorf("%w (%w)", err, deadErr)
|
||||
err = wrapBaseError(err, deadErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get a handle to the next component. O_DIRECTORY means we don't need
|
||||
// to use O_PATH.
|
||||
var nextDir *os.File
|
||||
if hasOpenat2() {
|
||||
nextDir, err = openat2File(currentDir, part, &unix.OpenHow{
|
||||
Flags: unix.O_NOFOLLOW | unix.O_DIRECTORY | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_NO_XDEV,
|
||||
})
|
||||
} else {
|
||||
nextDir, err = openatFile(currentDir, part, unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
|
||||
// It's possible that the directory we just opened was swapped by an
|
||||
// attacker. Unfortunately there isn't much we can do to protect
|
||||
// against this, and MkdirAll's behaviour is that we will reuse
|
||||
// existing directories anyway so the need to protect against this is
|
||||
// incredibly limited (and arguably doesn't even deserve mention here).
|
||||
//
|
||||
// Ideally we might want to check that the owner and mode match what we
|
||||
// would've created -- unfortunately, it is non-trivial to verify that
|
||||
// the owner and mode of the created directory match. While plain Unix
|
||||
// DAC rules seem simple enough to emulate, there are a bunch of other
|
||||
// factors that can change the mode or owner of created directories
|
||||
// (default POSIX ACLs, mount options like uid=1,gid=2,umask=0 on
|
||||
// filesystems like vfat, etc etc). We used to try to verify this but
|
||||
// it just lead to a series of spurious errors.
|
||||
//
|
||||
// We could also check that the directory is non-empty, but
|
||||
// unfortunately some pseduofilesystems (like cgroupfs) create
|
||||
// non-empty directories, which would result in different spurious
|
||||
// errors.
|
||||
}
|
||||
return currentDir, nil
|
||||
}
|
||||
|
||||
// MkdirAll is a race-safe alternative to the [os.MkdirAll] function,
|
||||
// where the new directory is guaranteed to be within the root directory (if an
|
||||
// attacker can move directories from inside the root to outside the root, the
|
||||
// created directory tree might be outside of the root but the key constraint
|
||||
// is that at no point will we walk outside of the directory tree we are
|
||||
// creating).
|
||||
//
|
||||
// Effectively, MkdirAll(root, unsafePath, mode) is equivalent to
|
||||
//
|
||||
// path, _ := securejoin.SecureJoin(root, unsafePath)
|
||||
// err := os.MkdirAll(path, mode)
|
||||
//
|
||||
// But is much safer. The above implementation is unsafe because if an attacker
|
||||
// can modify the filesystem tree between [SecureJoin] and [os.MkdirAll], it is
|
||||
// possible for MkdirAll to resolve unsafe symlink components and create
|
||||
// directories outside of the root.
|
||||
//
|
||||
// If you plan to open the directory after you have created it or want to use
|
||||
// an open directory handle as the root, you should use [MkdirAllHandle] instead.
|
||||
// This function is a wrapper around [MkdirAllHandle].
|
||||
//
|
||||
// NOTE: The mode argument must be set the unix mode bits (unix.S_I...), not
|
||||
// the Go generic mode bits ([os.FileMode]...).
|
||||
func MkdirAll(root, unsafePath string, mode int) error {
|
||||
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rootDir.Close()
|
||||
|
||||
f, err := MkdirAllHandle(rootDir, unsafePath, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = f.Close()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// OpenatInRoot is equivalent to [OpenInRoot], except that the root is provided
|
||||
// using an *[os.File] handle, to ensure that the correct root directory is used.
|
||||
func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error) {
|
||||
handle, err := completeLookupInRoot(root, unsafePath)
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "securejoin.OpenInRoot", Path: unsafePath, Err: err}
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// OpenInRoot safely opens the provided unsafePath within the root.
|
||||
// Effectively, OpenInRoot(root, unsafePath) is equivalent to
|
||||
//
|
||||
// path, _ := securejoin.SecureJoin(root, unsafePath)
|
||||
// handle, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC)
|
||||
//
|
||||
// But is much safer. The above implementation is unsafe because if an attacker
|
||||
// can modify the filesystem tree between [SecureJoin] and [os.OpenFile], it is
|
||||
// possible for the returned file to be outside of the root.
|
||||
//
|
||||
// Note that the returned handle is an O_PATH handle, meaning that only a very
|
||||
// limited set of operations will work on the handle. This is done to avoid
|
||||
// accidentally opening an untrusted file that could cause issues (such as a
|
||||
// disconnected TTY that could cause a DoS, or some other issue). In order to
|
||||
// use the returned handle, you can "upgrade" it to a proper handle using
|
||||
// [Reopen].
|
||||
func OpenInRoot(root, unsafePath string) (*os.File, error) {
|
||||
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rootDir.Close()
|
||||
return OpenatInRoot(rootDir, unsafePath)
|
||||
}
|
||||
|
||||
// Reopen takes an *[os.File] handle and re-opens it through /proc/self/fd.
|
||||
// Reopen(file, flags) is effectively equivalent to
|
||||
//
|
||||
// fdPath := fmt.Sprintf("/proc/self/fd/%d", file.Fd())
|
||||
// os.OpenFile(fdPath, flags|unix.O_CLOEXEC)
|
||||
//
|
||||
// But with some extra hardenings to ensure that we are not tricked by a
|
||||
// maliciously-configured /proc mount. While this attack scenario is not
|
||||
// common, in container runtimes it is possible for higher-level runtimes to be
|
||||
// tricked into configuring an unsafe /proc that can be used to attack file
|
||||
// operations. See [CVE-2019-19921] for more details.
|
||||
//
|
||||
// [CVE-2019-19921]: https://github.com/advisories/GHSA-fh74-hm69-rqjw
|
||||
func Reopen(handle *os.File, flags int) (*os.File, error) {
|
||||
procRoot, err := getProcRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We can't operate on /proc/thread-self/fd/$n directly when doing a
|
||||
// re-open, so we need to open /proc/thread-self/fd and then open a single
|
||||
// final component.
|
||||
procFdDir, closer, err := procThreadSelf(procRoot, "fd/")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get safe /proc/thread-self/fd handle: %w", err)
|
||||
}
|
||||
defer procFdDir.Close()
|
||||
defer closer()
|
||||
|
||||
// Try to detect if there is a mount on top of the magic-link we are about
|
||||
// to open. If we are using unsafeHostProcRoot(), this could change after
|
||||
// we check it (and there's nothing we can do about that) but for
|
||||
// privateProcRoot() this should be guaranteed to be safe (at least since
|
||||
// Linux 5.12[1], when anonymous mount namespaces were completely isolated
|
||||
// from external mounts including mount propagation events).
|
||||
//
|
||||
// [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts
|
||||
// onto targets that reside on shared mounts").
|
||||
fdStr := strconv.Itoa(int(handle.Fd()))
|
||||
if err := checkSymlinkOvermount(procRoot, procFdDir, fdStr); err != nil {
|
||||
return nil, fmt.Errorf("check safety of /proc/thread-self/fd/%s magiclink: %w", fdStr, err)
|
||||
}
|
||||
|
||||
flags |= unix.O_CLOEXEC
|
||||
// Rather than just wrapping openatFile, open-code it so we can copy
|
||||
// handle.Name().
|
||||
reopenFd, err := unix.Openat(int(procFdDir.Fd()), fdStr, flags, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reopen fd %d: %w", handle.Fd(), err)
|
||||
}
|
||||
return os.NewFile(uintptr(reopenFd), handle.Name()), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var hasOpenat2 = sync_OnceValue(func() bool {
|
||||
fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_IN_ROOT,
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_ = unix.Close(fd)
|
||||
return true
|
||||
})
|
||||
|
||||
func scopedLookupShouldRetry(how *unix.OpenHow, err error) bool {
|
||||
// RESOLVE_IN_ROOT (and RESOLVE_BENEATH) can return -EAGAIN if we resolve
|
||||
// ".." while a mount or rename occurs anywhere on the system. This could
|
||||
// happen spuriously, or as the result of an attacker trying to mess with
|
||||
// us during lookup.
|
||||
//
|
||||
// In addition, scoped lookups have a "safety check" at the end of
|
||||
// complete_walk which will return -EXDEV if the final path is not in the
|
||||
// root.
|
||||
return how.Resolve&(unix.RESOLVE_IN_ROOT|unix.RESOLVE_BENEATH) != 0 &&
|
||||
(errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EXDEV))
|
||||
}
|
||||
|
||||
const scopedLookupMaxRetries = 10
|
||||
|
||||
func openat2File(dir *os.File, path string, how *unix.OpenHow) (*os.File, error) {
|
||||
fullPath := dir.Name() + "/" + path
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
how.Flags |= unix.O_CLOEXEC
|
||||
var tries int
|
||||
for tries < scopedLookupMaxRetries {
|
||||
fd, err := unix.Openat2(int(dir.Fd()), path, how)
|
||||
if err != nil {
|
||||
if scopedLookupShouldRetry(how, err) {
|
||||
// We retry a couple of times to avoid the spurious errors, and
|
||||
// if we are being attacked then returning -EAGAIN is the best
|
||||
// we can do.
|
||||
tries++
|
||||
continue
|
||||
}
|
||||
return nil, &os.PathError{Op: "openat2", Path: fullPath, Err: err}
|
||||
}
|
||||
// If we are using RESOLVE_IN_ROOT, the name we generated may be wrong.
|
||||
// NOTE: The procRoot code MUST NOT use RESOLVE_IN_ROOT, otherwise
|
||||
// you'll get infinite recursion here.
|
||||
if how.Resolve&unix.RESOLVE_IN_ROOT == unix.RESOLVE_IN_ROOT {
|
||||
if actualPath, err := rawProcSelfFdReadlink(fd); err == nil {
|
||||
fullPath = actualPath
|
||||
}
|
||||
}
|
||||
return os.NewFile(uintptr(fd), fullPath), nil
|
||||
}
|
||||
return nil, &os.PathError{Op: "openat2", Path: fullPath, Err: errPossibleAttack}
|
||||
}
|
||||
|
||||
func lookupOpenat2(root *os.File, unsafePath string, partial bool) (*os.File, string, error) {
|
||||
if !partial {
|
||||
file, err := openat2File(root, unsafePath, &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS,
|
||||
})
|
||||
return file, "", err
|
||||
}
|
||||
return partialLookupOpenat2(root, unsafePath)
|
||||
}
|
||||
|
||||
// partialLookupOpenat2 is an alternative implementation of
|
||||
// partialLookupInRoot, using openat2(RESOLVE_IN_ROOT) to more safely get a
|
||||
// handle to the deepest existing child of the requested path within the root.
|
||||
func partialLookupOpenat2(root *os.File, unsafePath string) (*os.File, string, error) {
|
||||
// TODO: Implement this as a git-bisect-like binary search.
|
||||
|
||||
unsafePath = filepath.ToSlash(unsafePath) // noop
|
||||
endIdx := len(unsafePath)
|
||||
var lastError error
|
||||
for endIdx > 0 {
|
||||
subpath := unsafePath[:endIdx]
|
||||
|
||||
handle, err := openat2File(root, subpath, &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS,
|
||||
})
|
||||
if err == nil {
|
||||
// Jump over the slash if we have a non-"" remainingPath.
|
||||
if endIdx < len(unsafePath) {
|
||||
endIdx += 1
|
||||
}
|
||||
// We found a subpath!
|
||||
return handle, unsafePath[endIdx:], lastError
|
||||
}
|
||||
if errors.Is(err, unix.ENOENT) || errors.Is(err, unix.ENOTDIR) {
|
||||
// That path doesn't exist, let's try the next directory up.
|
||||
endIdx = strings.LastIndexByte(subpath, '/')
|
||||
lastError = err
|
||||
continue
|
||||
}
|
||||
return nil, "", fmt.Errorf("open subpath: %w", err)
|
||||
}
|
||||
// If we couldn't open anything, the whole subpath is missing. Return a
|
||||
// copy of the root fd so that the caller doesn't close this one by
|
||||
// accident.
|
||||
rootClone, err := dupFile(root)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return rootClone, unsafePath, lastError
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func dupFile(f *os.File) (*os.File, error) {
|
||||
fd, err := unix.FcntlInt(f.Fd(), unix.F_DUPFD_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("fcntl(F_DUPFD_CLOEXEC)", err)
|
||||
}
|
||||
return os.NewFile(uintptr(fd), f.Name()), nil
|
||||
}
|
||||
|
||||
func openatFile(dir *os.File, path string, flags int, mode int) (*os.File, error) {
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.O_CLOEXEC
|
||||
fd, err := unix.Openat(int(dir.Fd()), path, flags, uint32(mode))
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "openat", Path: dir.Name() + "/" + path, Err: err}
|
||||
}
|
||||
// All of the paths we use with openatFile(2) are guaranteed to be
|
||||
// lexically safe, so we can use path.Join here.
|
||||
fullPath := filepath.Join(dir.Name(), path)
|
||||
return os.NewFile(uintptr(fd), fullPath), nil
|
||||
}
|
||||
|
||||
func fstatatFile(dir *os.File, path string, flags int) (unix.Stat_t, error) {
|
||||
var stat unix.Stat_t
|
||||
if err := unix.Fstatat(int(dir.Fd()), path, &stat, flags); err != nil {
|
||||
return stat, &os.PathError{Op: "fstatat", Path: dir.Name() + "/" + path, Err: err}
|
||||
}
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
func readlinkatFile(dir *os.File, path string) (string, error) {
|
||||
size := 4096
|
||||
for {
|
||||
linkBuf := make([]byte, size)
|
||||
n, err := unix.Readlinkat(int(dir.Fd()), path, linkBuf)
|
||||
if err != nil {
|
||||
return "", &os.PathError{Op: "readlinkat", Path: dir.Name() + "/" + path, Err: err}
|
||||
}
|
||||
if n != size {
|
||||
return string(linkBuf[:n]), nil
|
||||
}
|
||||
// Possible truncation, resize the buffer.
|
||||
size *= 2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,452 @@
|
|||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func fstat(f *os.File) (unix.Stat_t, error) {
|
||||
var stat unix.Stat_t
|
||||
if err := unix.Fstat(int(f.Fd()), &stat); err != nil {
|
||||
return stat, &os.PathError{Op: "fstat", Path: f.Name(), Err: err}
|
||||
}
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
func fstatfs(f *os.File) (unix.Statfs_t, error) {
|
||||
var statfs unix.Statfs_t
|
||||
if err := unix.Fstatfs(int(f.Fd()), &statfs); err != nil {
|
||||
return statfs, &os.PathError{Op: "fstatfs", Path: f.Name(), Err: err}
|
||||
}
|
||||
return statfs, nil
|
||||
}
|
||||
|
||||
// The kernel guarantees that the root inode of a procfs mount has an
|
||||
// f_type of PROC_SUPER_MAGIC and st_ino of PROC_ROOT_INO.
|
||||
const (
|
||||
procSuperMagic = 0x9fa0 // PROC_SUPER_MAGIC
|
||||
procRootIno = 1 // PROC_ROOT_INO
|
||||
)
|
||||
|
||||
func verifyProcRoot(procRoot *os.File) error {
|
||||
if statfs, err := fstatfs(procRoot); err != nil {
|
||||
return err
|
||||
} else if statfs.Type != procSuperMagic {
|
||||
return fmt.Errorf("%w: incorrect procfs root filesystem type 0x%x", errUnsafeProcfs, statfs.Type)
|
||||
}
|
||||
if stat, err := fstat(procRoot); err != nil {
|
||||
return err
|
||||
} else if stat.Ino != procRootIno {
|
||||
return fmt.Errorf("%w: incorrect procfs root inode number %d", errUnsafeProcfs, stat.Ino)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var hasNewMountApi = sync_OnceValue(func() bool {
|
||||
// All of the pieces of the new mount API we use (fsopen, fsconfig,
|
||||
// fsmount, open_tree) were added together in Linux 5.1[1,2], so we can
|
||||
// just check for one of the syscalls and the others should also be
|
||||
// available.
|
||||
//
|
||||
// Just try to use open_tree(2) to open a file without OPEN_TREE_CLONE.
|
||||
// This is equivalent to openat(2), but tells us if open_tree is
|
||||
// available (and thus all of the other basic new mount API syscalls).
|
||||
// open_tree(2) is most light-weight syscall to test here.
|
||||
//
|
||||
// [1]: merge commit 400913252d09
|
||||
// [2]: <https://lore.kernel.org/lkml/153754740781.17872.7869536526927736855.stgit@warthog.procyon.org.uk/>
|
||||
fd, err := unix.OpenTree(-int(unix.EBADF), "/", unix.OPEN_TREE_CLOEXEC)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_ = unix.Close(fd)
|
||||
return true
|
||||
})
|
||||
|
||||
func fsopen(fsName string, flags int) (*os.File, error) {
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.FSOPEN_CLOEXEC
|
||||
fd, err := unix.Fsopen(fsName, flags)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("fsopen "+fsName, err)
|
||||
}
|
||||
return os.NewFile(uintptr(fd), "fscontext:"+fsName), nil
|
||||
}
|
||||
|
||||
func fsmount(ctx *os.File, flags, mountAttrs int) (*os.File, error) {
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.FSMOUNT_CLOEXEC
|
||||
fd, err := unix.Fsmount(int(ctx.Fd()), flags, mountAttrs)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("fsmount "+ctx.Name(), err)
|
||||
}
|
||||
return os.NewFile(uintptr(fd), "fsmount:"+ctx.Name()), nil
|
||||
}
|
||||
|
||||
func newPrivateProcMount() (*os.File, error) {
|
||||
procfsCtx, err := fsopen("proc", unix.FSOPEN_CLOEXEC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer procfsCtx.Close()
|
||||
|
||||
// Try to configure hidepid=ptraceable,subset=pid if possible, but ignore errors.
|
||||
_ = unix.FsconfigSetString(int(procfsCtx.Fd()), "hidepid", "ptraceable")
|
||||
_ = unix.FsconfigSetString(int(procfsCtx.Fd()), "subset", "pid")
|
||||
|
||||
// Get an actual handle.
|
||||
if err := unix.FsconfigCreate(int(procfsCtx.Fd())); err != nil {
|
||||
return nil, os.NewSyscallError("fsconfig create procfs", err)
|
||||
}
|
||||
return fsmount(procfsCtx, unix.FSMOUNT_CLOEXEC, unix.MS_RDONLY|unix.MS_NODEV|unix.MS_NOEXEC|unix.MS_NOSUID)
|
||||
}
|
||||
|
||||
func openTree(dir *os.File, path string, flags uint) (*os.File, error) {
|
||||
dirFd := -int(unix.EBADF)
|
||||
dirName := "."
|
||||
if dir != nil {
|
||||
dirFd = int(dir.Fd())
|
||||
dirName = dir.Name()
|
||||
}
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.OPEN_TREE_CLOEXEC
|
||||
fd, err := unix.OpenTree(dirFd, path, flags)
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "open_tree", Path: path, Err: err}
|
||||
}
|
||||
return os.NewFile(uintptr(fd), dirName+"/"+path), nil
|
||||
}
|
||||
|
||||
func clonePrivateProcMount() (_ *os.File, Err error) {
|
||||
// Try to make a clone without using AT_RECURSIVE if we can. If this works,
|
||||
// we can be sure there are no over-mounts and so if the root is valid then
|
||||
// we're golden. Otherwise, we have to deal with over-mounts.
|
||||
procfsHandle, err := openTree(nil, "/proc", unix.OPEN_TREE_CLONE)
|
||||
if err != nil || hookForcePrivateProcRootOpenTreeAtRecursive(procfsHandle) {
|
||||
procfsHandle, err = openTree(nil, "/proc", unix.OPEN_TREE_CLONE|unix.AT_RECURSIVE)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating a detached procfs clone: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = procfsHandle.Close()
|
||||
}
|
||||
}()
|
||||
if err := verifyProcRoot(procfsHandle); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return procfsHandle, nil
|
||||
}
|
||||
|
||||
func privateProcRoot() (*os.File, error) {
|
||||
if !hasNewMountApi() || hookForceGetProcRootUnsafe() {
|
||||
return nil, fmt.Errorf("new mount api: %w", unix.ENOTSUP)
|
||||
}
|
||||
// Try to create a new procfs mount from scratch if we can. This ensures we
|
||||
// can get a procfs mount even if /proc is fake (for whatever reason).
|
||||
procRoot, err := newPrivateProcMount()
|
||||
if err != nil || hookForcePrivateProcRootOpenTree(procRoot) {
|
||||
// Try to clone /proc then...
|
||||
procRoot, err = clonePrivateProcMount()
|
||||
}
|
||||
return procRoot, err
|
||||
}
|
||||
|
||||
func unsafeHostProcRoot() (_ *os.File, Err error) {
|
||||
procRoot, err := os.OpenFile("/proc", unix.O_PATH|unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = procRoot.Close()
|
||||
}
|
||||
}()
|
||||
if err := verifyProcRoot(procRoot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return procRoot, nil
|
||||
}
|
||||
|
||||
func doGetProcRoot() (*os.File, error) {
|
||||
procRoot, err := privateProcRoot()
|
||||
if err != nil {
|
||||
// Fall back to using a /proc handle if making a private mount failed.
|
||||
// If we have openat2, at least we can avoid some kinds of over-mount
|
||||
// attacks, but without openat2 there's not much we can do.
|
||||
procRoot, err = unsafeHostProcRoot()
|
||||
}
|
||||
return procRoot, err
|
||||
}
|
||||
|
||||
var getProcRoot = sync_OnceValues(func() (*os.File, error) {
|
||||
return doGetProcRoot()
|
||||
})
|
||||
|
||||
var hasProcThreadSelf = sync_OnceValue(func() bool {
|
||||
return unix.Access("/proc/thread-self/", unix.F_OK) == nil
|
||||
})
|
||||
|
||||
var errUnsafeProcfs = errors.New("unsafe procfs detected")
|
||||
|
||||
type procThreadSelfCloser func()
|
||||
|
||||
// procThreadSelf returns a handle to /proc/thread-self/<subpath> (or an
|
||||
// equivalent handle on older kernels where /proc/thread-self doesn't exist).
|
||||
// Once finished with the handle, you must call the returned closer function
|
||||
// (runtime.UnlockOSThread). You must not pass the returned *os.File to other
|
||||
// Go threads or use the handle after calling the closer.
|
||||
//
|
||||
// This is similar to ProcThreadSelf from runc, but with extra hardening
|
||||
// applied and using *os.File.
|
||||
func procThreadSelf(procRoot *os.File, subpath string) (_ *os.File, _ procThreadSelfCloser, Err error) {
|
||||
// We need to lock our thread until the caller is done with the handle
|
||||
// because between getting the handle and using it we could get interrupted
|
||||
// by the Go runtime and hit the case where the underlying thread is
|
||||
// swapped out and the original thread is killed, resulting in
|
||||
// pull-your-hair-out-hard-to-debug issues in the caller.
|
||||
runtime.LockOSThread()
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
runtime.UnlockOSThread()
|
||||
}
|
||||
}()
|
||||
|
||||
// Figure out what prefix we want to use.
|
||||
threadSelf := "thread-self/"
|
||||
if !hasProcThreadSelf() || hookForceProcSelfTask() {
|
||||
/// Pre-3.17 kernels don't have /proc/thread-self, so do it manually.
|
||||
threadSelf = "self/task/" + strconv.Itoa(unix.Gettid()) + "/"
|
||||
if _, err := fstatatFile(procRoot, threadSelf, unix.AT_SYMLINK_NOFOLLOW); err != nil || hookForceProcSelf() {
|
||||
// In this case, we running in a pid namespace that doesn't match
|
||||
// the /proc mount we have. This can happen inside runc.
|
||||
//
|
||||
// Unfortunately, there is no nice way to get the correct TID to
|
||||
// use here because of the age of the kernel, so we have to just
|
||||
// use /proc/self and hope that it works.
|
||||
threadSelf = "self/"
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the handle.
|
||||
var (
|
||||
handle *os.File
|
||||
err error
|
||||
)
|
||||
if hasOpenat2() {
|
||||
// We prefer being able to use RESOLVE_NO_XDEV if we can, to be
|
||||
// absolutely sure we are operating on a clean /proc handle that
|
||||
// doesn't have any cheeky overmounts that could trick us (including
|
||||
// symlink mounts on top of /proc/thread-self). RESOLVE_BENEATH isn't
|
||||
// strictly needed, but just use it since we have it.
|
||||
//
|
||||
// NOTE: /proc/self is technically a magic-link (the contents of the
|
||||
// symlink are generated dynamically), but it doesn't use
|
||||
// nd_jump_link() so RESOLVE_NO_MAGICLINKS allows it.
|
||||
//
|
||||
// NOTE: We MUST NOT use RESOLVE_IN_ROOT here, as openat2File uses
|
||||
// procSelfFdReadlink to clean up the returned f.Name() if we use
|
||||
// RESOLVE_IN_ROOT (which would lead to an infinite recursion).
|
||||
handle, err = openat2File(procRoot, threadSelf+subpath, &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_NOFOLLOW | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_MAGICLINKS,
|
||||
})
|
||||
if err != nil {
|
||||
// TODO: Once we bump the minimum Go version to 1.20, we can use
|
||||
// multiple %w verbs for this wrapping. For now we need to use a
|
||||
// compatibility shim for older Go versions.
|
||||
//err = fmt.Errorf("%w: %w", errUnsafeProcfs, err)
|
||||
return nil, nil, wrapBaseError(err, errUnsafeProcfs)
|
||||
}
|
||||
} else {
|
||||
handle, err = openatFile(procRoot, threadSelf+subpath, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
// TODO: Once we bump the minimum Go version to 1.20, we can use
|
||||
// multiple %w verbs for this wrapping. For now we need to use a
|
||||
// compatibility shim for older Go versions.
|
||||
//err = fmt.Errorf("%w: %w", errUnsafeProcfs, err)
|
||||
return nil, nil, wrapBaseError(err, errUnsafeProcfs)
|
||||
}
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = handle.Close()
|
||||
}
|
||||
}()
|
||||
// We can't detect bind-mounts of different parts of procfs on top of
|
||||
// /proc (a-la RESOLVE_NO_XDEV), but we can at least be sure that we
|
||||
// aren't on the wrong filesystem here.
|
||||
if statfs, err := fstatfs(handle); err != nil {
|
||||
return nil, nil, err
|
||||
} else if statfs.Type != procSuperMagic {
|
||||
return nil, nil, fmt.Errorf("%w: incorrect /proc/self/fd filesystem type 0x%x", errUnsafeProcfs, statfs.Type)
|
||||
}
|
||||
}
|
||||
return handle, runtime.UnlockOSThread, nil
|
||||
}
|
||||
|
||||
// STATX_MNT_ID_UNIQUE is provided in golang.org/x/sys@v0.20.0, but in order to
|
||||
// avoid bumping the requirement for a single constant we can just define it
|
||||
// ourselves.
|
||||
const STATX_MNT_ID_UNIQUE = 0x4000
|
||||
|
||||
var hasStatxMountId = sync_OnceValue(func() bool {
|
||||
var (
|
||||
stx unix.Statx_t
|
||||
// We don't care which mount ID we get. The kernel will give us the
|
||||
// unique one if it is supported.
|
||||
wantStxMask uint32 = STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
|
||||
)
|
||||
err := unix.Statx(-int(unix.EBADF), "/", 0, int(wantStxMask), &stx)
|
||||
return err == nil && stx.Mask&wantStxMask != 0
|
||||
})
|
||||
|
||||
func getMountId(dir *os.File, path string) (uint64, error) {
|
||||
// If we don't have statx(STATX_MNT_ID*) support, we can't do anything.
|
||||
if !hasStatxMountId() {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var (
|
||||
stx unix.Statx_t
|
||||
// We don't care which mount ID we get. The kernel will give us the
|
||||
// unique one if it is supported.
|
||||
wantStxMask uint32 = STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
|
||||
)
|
||||
|
||||
err := unix.Statx(int(dir.Fd()), path, unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW, int(wantStxMask), &stx)
|
||||
if stx.Mask&wantStxMask == 0 {
|
||||
// It's not a kernel limitation, for some reason we couldn't get a
|
||||
// mount ID. Assume it's some kind of attack.
|
||||
err = fmt.Errorf("%w: could not get mount id", errUnsafeProcfs)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, &os.PathError{Op: "statx(STATX_MNT_ID_...)", Path: dir.Name() + "/" + path, Err: err}
|
||||
}
|
||||
return stx.Mnt_id, nil
|
||||
}
|
||||
|
||||
func checkSymlinkOvermount(procRoot *os.File, dir *os.File, path string) error {
|
||||
// Get the mntId of our procfs handle.
|
||||
expectedMountId, err := getMountId(procRoot, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Get the mntId of the target magic-link.
|
||||
gotMountId, err := getMountId(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// As long as the directory mount is alive, even with wrapping mount IDs,
|
||||
// we would expect to see a different mount ID here. (Of course, if we're
|
||||
// using unsafeHostProcRoot() then an attaker could change this after we
|
||||
// did this check.)
|
||||
if expectedMountId != gotMountId {
|
||||
return fmt.Errorf("%w: symlink %s/%s has an overmount obscuring the real link (mount ids do not match %d != %d)", errUnsafeProcfs, dir.Name(), path, expectedMountId, gotMountId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doRawProcSelfFdReadlink(procRoot *os.File, fd int) (string, error) {
|
||||
fdPath := fmt.Sprintf("fd/%d", fd)
|
||||
procFdLink, closer, err := procThreadSelf(procRoot, fdPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get safe /proc/thread-self/%s handle: %w", fdPath, err)
|
||||
}
|
||||
defer procFdLink.Close()
|
||||
defer closer()
|
||||
|
||||
// Try to detect if there is a mount on top of the magic-link. Since we use the handle directly
|
||||
// provide to the closure. If the closure uses the handle directly, this
|
||||
// should be safe in general (a mount on top of the path afterwards would
|
||||
// not affect the handle itself) and will definitely be safe if we are
|
||||
// using privateProcRoot() (at least since Linux 5.12[1], when anonymous
|
||||
// mount namespaces were completely isolated from external mounts including
|
||||
// mount propagation events).
|
||||
//
|
||||
// [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts
|
||||
// onto targets that reside on shared mounts").
|
||||
if err := checkSymlinkOvermount(procRoot, procFdLink, ""); err != nil {
|
||||
return "", fmt.Errorf("check safety of /proc/thread-self/fd/%d magiclink: %w", fd, err)
|
||||
}
|
||||
|
||||
// readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See Linux commit
|
||||
// 65cfc6722361 ("readlinkat(), fchownat() and fstatat() with empty
|
||||
// relative pathnames").
|
||||
return readlinkatFile(procFdLink, "")
|
||||
}
|
||||
|
||||
func rawProcSelfFdReadlink(fd int) (string, error) {
|
||||
procRoot, err := getProcRoot()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return doRawProcSelfFdReadlink(procRoot, fd)
|
||||
}
|
||||
|
||||
func procSelfFdReadlink(f *os.File) (string, error) {
|
||||
return rawProcSelfFdReadlink(int(f.Fd()))
|
||||
}
|
||||
|
||||
var (
|
||||
errPossibleBreakout = errors.New("possible breakout detected")
|
||||
errInvalidDirectory = errors.New("wandered into deleted directory")
|
||||
errDeletedInode = errors.New("cannot verify path of deleted inode")
|
||||
)
|
||||
|
||||
func isDeadInode(file *os.File) error {
|
||||
// If the nlink of a file drops to 0, there is an attacker deleting
|
||||
// directories during our walk, which could result in weird /proc values.
|
||||
// It's better to error out in this case.
|
||||
stat, err := fstat(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check for dead inode: %w", err)
|
||||
}
|
||||
if stat.Nlink == 0 {
|
||||
err := errDeletedInode
|
||||
if stat.Mode&unix.S_IFMT == unix.S_IFDIR {
|
||||
err = errInvalidDirectory
|
||||
}
|
||||
return fmt.Errorf("%w %q", err, file.Name())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkProcSelfFdPath(path string, file *os.File) error {
|
||||
if err := isDeadInode(file); err != nil {
|
||||
return err
|
||||
}
|
||||
actualPath, err := procSelfFdReadlink(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get path of handle: %w", err)
|
||||
}
|
||||
if actualPath != path {
|
||||
return fmt.Errorf("%w: handle path %q doesn't match expected path %q", errPossibleBreakout, actualPath, path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test hooks used in the procfs tests to verify that the fallback logic works.
|
||||
// See testing_mocks_linux_test.go and procfs_linux_test.go for more details.
|
||||
var (
|
||||
hookForcePrivateProcRootOpenTree = hookDummyFile
|
||||
hookForcePrivateProcRootOpenTreeAtRecursive = hookDummyFile
|
||||
hookForceGetProcRootUnsafe = hookDummy
|
||||
|
||||
hookForceProcSelfTask = hookDummy
|
||||
hookForceProcSelf = hookDummy
|
||||
)
|
||||
|
||||
func hookDummy() bool { return false }
|
||||
func hookDummyFile(_ *os.File) bool { return false }
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2017 SUSE LLC. All rights reserved.
|
||||
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
|
@ -10,19 +10,19 @@ import "os"
|
|||
// are several projects (umoci and go-mtree) that are using this sort of
|
||||
// interface.
|
||||
|
||||
// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is
|
||||
// equivalent to using the standard os.* family of functions. This is mainly
|
||||
// VFS is the minimal interface necessary to use [SecureJoinVFS]. A nil VFS is
|
||||
// equivalent to using the standard [os].* family of functions. This is mainly
|
||||
// used for the purposes of mock testing, but also can be used to otherwise use
|
||||
// SecureJoin with VFS-like system.
|
||||
// [SecureJoinVFS] with VFS-like system.
|
||||
type VFS interface {
|
||||
// Lstat returns a FileInfo describing the named file. If the file is a
|
||||
// symbolic link, the returned FileInfo describes the symbolic link. Lstat
|
||||
// makes no attempt to follow the link. These semantics are identical to
|
||||
// os.Lstat.
|
||||
// Lstat returns an [os.FileInfo] describing the named file. If the
|
||||
// file is a symbolic link, the returned [os.FileInfo] describes the
|
||||
// symbolic link. Lstat makes no attempt to follow the link.
|
||||
// The semantics are identical to [os.Lstat].
|
||||
Lstat(name string) (os.FileInfo, error)
|
||||
|
||||
// Readlink returns the destination of the named symbolic link. These
|
||||
// semantics are identical to os.Readlink.
|
||||
// Readlink returns the destination of the named symbolic link.
|
||||
// The semantics are identical to [os.Readlink].
|
||||
Readlink(name string) (string, error)
|
||||
}
|
||||
|
||||
|
|
@ -30,12 +30,6 @@ type VFS interface {
|
|||
// module.
|
||||
type osVFS struct{}
|
||||
|
||||
// Lstat returns a FileInfo describing the named file. If the file is a
|
||||
// symbolic link, the returned FileInfo describes the symbolic link. Lstat
|
||||
// makes no attempt to follow the link. These semantics are identical to
|
||||
// os.Lstat.
|
||||
func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) }
|
||||
|
||||
// Readlink returns the destination of the named symbolic link. These
|
||||
// semantics are identical to os.Readlink.
|
||||
func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build dragonfly || freebsd || linux || netbsd || openbsd
|
||||
//go:build dragonfly || freebsd || linux || netbsd
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
|||
|
|
@ -231,3 +231,8 @@ func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) {
|
|||
func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error {
|
||||
return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value))
|
||||
}
|
||||
|
||||
// IoctlLoopConfigure configures all loop device parameters in a single step
|
||||
func IoctlLoopConfigure(fd int, value *LoopConfig) error {
|
||||
return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ struct ltchars {
|
|||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/net_namespace.h>
|
||||
#include <linux/nfc.h>
|
||||
|
|
@ -283,10 +284,6 @@ struct ltchars {
|
|||
#include <asm/termbits.h>
|
||||
#endif
|
||||
|
||||
#ifndef MSG_FASTOPEN
|
||||
#define MSG_FASTOPEN 0x20000000
|
||||
#endif
|
||||
|
||||
#ifndef PTRACE_GETREGS
|
||||
#define PTRACE_GETREGS 0xc
|
||||
#endif
|
||||
|
|
@ -295,14 +292,6 @@ struct ltchars {
|
|||
#define PTRACE_SETREGS 0xd
|
||||
#endif
|
||||
|
||||
#ifndef SOL_NETLINK
|
||||
#define SOL_NETLINK 270
|
||||
#endif
|
||||
|
||||
#ifndef SOL_SMC
|
||||
#define SOL_SMC 286
|
||||
#endif
|
||||
|
||||
#ifdef SOL_BLUETOOTH
|
||||
// SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h
|
||||
// but it is already in bluetooth_linux.go
|
||||
|
|
@ -319,10 +308,23 @@ struct ltchars {
|
|||
#undef TIPC_WAIT_FOREVER
|
||||
#define TIPC_WAIT_FOREVER 0xffffffff
|
||||
|
||||
// Copied from linux/l2tp.h
|
||||
// Including linux/l2tp.h here causes conflicts between linux/in.h
|
||||
// and netinet/in.h included via net/route.h above.
|
||||
#define IPPROTO_L2TP 115
|
||||
// Copied from linux/netfilter/nf_nat.h
|
||||
// Including linux/netfilter/nf_nat.h here causes conflicts between linux/in.h
|
||||
// and netinet/in.h.
|
||||
#define NF_NAT_RANGE_MAP_IPS (1 << 0)
|
||||
#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1)
|
||||
#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2)
|
||||
#define NF_NAT_RANGE_PERSISTENT (1 << 3)
|
||||
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
|
||||
#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5)
|
||||
#define NF_NAT_RANGE_NETMAP (1 << 6)
|
||||
#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
|
||||
(NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
|
||||
#define NF_NAT_RANGE_MASK \
|
||||
(NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \
|
||||
NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \
|
||||
NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \
|
||||
NF_NAT_RANGE_NETMAP)
|
||||
|
||||
// Copied from linux/hid.h.
|
||||
// Keep in sync with the size of the referenced fields.
|
||||
|
|
@ -519,6 +521,7 @@ ccflags="$@"
|
|||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
||||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
|
||||
$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ ||
|
||||
$2 == "LOOP_CONFIGURE" ||
|
||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ ||
|
||||
$2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ ||
|
||||
$2 ~ /^NFC_.*_(MAX)?SIZE$/ ||
|
||||
|
|
@ -560,7 +563,7 @@ ccflags="$@"
|
|||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ ||
|
||||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
|
||||
$2 ~ /^CLONE_[A-Z_]+/ ||
|
||||
$2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ &&
|
||||
$2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+|BPF_F_LINK)$/ &&
|
||||
$2 ~ /^(BPF|DLT)_/ ||
|
||||
$2 ~ /^AUDIT_/ ||
|
||||
$2 ~ /^(CLOCK|TIMER)_/ ||
|
||||
|
|
@ -581,7 +584,7 @@ ccflags="$@"
|
|||
$2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ ||
|
||||
$2 ~ /^KEYCTL_/ ||
|
||||
$2 ~ /^PERF_/ ||
|
||||
$2 ~ /^SECCOMP_MODE_/ ||
|
||||
$2 ~ /^SECCOMP_/ ||
|
||||
$2 ~ /^SEEK_/ ||
|
||||
$2 ~ /^SCHED_/ ||
|
||||
$2 ~ /^SPLICE_/ ||
|
||||
|
|
@ -602,6 +605,9 @@ ccflags="$@"
|
|||
$2 ~ /^FSOPT_/ ||
|
||||
$2 ~ /^WDIO[CFS]_/ ||
|
||||
$2 ~ /^NFN/ ||
|
||||
$2 !~ /^NFT_META_IIFTYPE/ &&
|
||||
$2 ~ /^NFT_/ ||
|
||||
$2 ~ /^NF_NAT_/ ||
|
||||
$2 ~ /^XDP_/ ||
|
||||
$2 ~ /^RWF_/ ||
|
||||
$2 ~ /^(HDIO|WIN|SMART)_/ ||
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf[:vallen-1]), nil
|
||||
return ByteSliceToString(buf[:vallen]), nil
|
||||
}
|
||||
|
||||
//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin && go1.12
|
||||
//go:build darwin
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
package unix
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
|
@ -169,25 +170,26 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
|||
func Uname(uname *Utsname) error {
|
||||
mib := []_C_int{CTL_KERN, KERN_OSTYPE}
|
||||
n := unsafe.Sizeof(uname.Sysname)
|
||||
if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil {
|
||||
// Suppress ENOMEM errors to be compatible with the C library __xuname() implementation.
|
||||
if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
|
||||
return err
|
||||
}
|
||||
|
||||
mib = []_C_int{CTL_KERN, KERN_HOSTNAME}
|
||||
n = unsafe.Sizeof(uname.Nodename)
|
||||
if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil {
|
||||
if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
|
||||
return err
|
||||
}
|
||||
|
||||
mib = []_C_int{CTL_KERN, KERN_OSRELEASE}
|
||||
n = unsafe.Sizeof(uname.Release)
|
||||
if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil {
|
||||
if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
|
||||
return err
|
||||
}
|
||||
|
||||
mib = []_C_int{CTL_KERN, KERN_VERSION}
|
||||
n = unsafe.Sizeof(uname.Version)
|
||||
if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil {
|
||||
if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +207,7 @@ func Uname(uname *Utsname) error {
|
|||
|
||||
mib = []_C_int{CTL_HW, HW_MACHINE}
|
||||
n = unsafe.Sizeof(uname.Machine)
|
||||
if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil {
|
||||
if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,15 +61,23 @@ func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) (
|
|||
}
|
||||
|
||||
//sys fchmodat(dirfd int, path string, mode uint32) (err error)
|
||||
//sys fchmodat2(dirfd int, path string, mode uint32, flags int) (err error)
|
||||
|
||||
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
|
||||
// Linux fchmodat doesn't support the flags parameter. Mimick glibc's behavior
|
||||
// and check the flags. Otherwise the mode would be applied to the symlink
|
||||
// destination which is not what the user expects.
|
||||
if flags&^AT_SYMLINK_NOFOLLOW != 0 {
|
||||
return EINVAL
|
||||
} else if flags&AT_SYMLINK_NOFOLLOW != 0 {
|
||||
return EOPNOTSUPP
|
||||
func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
|
||||
// Linux fchmodat doesn't support the flags parameter, but fchmodat2 does.
|
||||
// Try fchmodat2 if flags are specified.
|
||||
if flags != 0 {
|
||||
err := fchmodat2(dirfd, path, mode, flags)
|
||||
if err == ENOSYS {
|
||||
// fchmodat2 isn't available. If the flags are known to be valid,
|
||||
// return EOPNOTSUPP to indicate that fchmodat doesn't support them.
|
||||
if flags&^(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 {
|
||||
return EINVAL
|
||||
} else if flags&(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 {
|
||||
return EOPNOTSUPP
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return fchmodat(dirfd, path, mode)
|
||||
}
|
||||
|
|
@ -1302,7 +1310,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
}
|
||||
return string(buf[:vallen-1]), nil
|
||||
return ByteSliceToString(buf[:vallen]), nil
|
||||
}
|
||||
|
||||
func GetsockoptTpacketStats(fd, level, opt int) (*TpacketStats, error) {
|
||||
|
|
@ -1841,6 +1849,105 @@ func Dup2(oldfd, newfd int) error {
|
|||
//sys Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error)
|
||||
//sys Fsopen(fsName string, flags int) (fd int, err error)
|
||||
//sys Fspick(dirfd int, pathName string, flags int) (fd int, err error)
|
||||
|
||||
//sys fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error)
|
||||
|
||||
func fsconfigCommon(fd int, cmd uint, key string, value *byte, aux int) (err error) {
|
||||
var keyp *byte
|
||||
if keyp, err = BytePtrFromString(key); err != nil {
|
||||
return
|
||||
}
|
||||
return fsconfig(fd, cmd, keyp, value, aux)
|
||||
}
|
||||
|
||||
// FsconfigSetFlag is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_SET_FLAG.
|
||||
//
|
||||
// fd is the filesystem context to act upon.
|
||||
// key the parameter key to set.
|
||||
func FsconfigSetFlag(fd int, key string) (err error) {
|
||||
return fsconfigCommon(fd, FSCONFIG_SET_FLAG, key, nil, 0)
|
||||
}
|
||||
|
||||
// FsconfigSetString is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_SET_STRING.
|
||||
//
|
||||
// fd is the filesystem context to act upon.
|
||||
// key the parameter key to set.
|
||||
// value is the parameter value to set.
|
||||
func FsconfigSetString(fd int, key string, value string) (err error) {
|
||||
var valuep *byte
|
||||
if valuep, err = BytePtrFromString(value); err != nil {
|
||||
return
|
||||
}
|
||||
return fsconfigCommon(fd, FSCONFIG_SET_STRING, key, valuep, 0)
|
||||
}
|
||||
|
||||
// FsconfigSetBinary is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_SET_BINARY.
|
||||
//
|
||||
// fd is the filesystem context to act upon.
|
||||
// key the parameter key to set.
|
||||
// value is the parameter value to set.
|
||||
func FsconfigSetBinary(fd int, key string, value []byte) (err error) {
|
||||
if len(value) == 0 {
|
||||
return EINVAL
|
||||
}
|
||||
return fsconfigCommon(fd, FSCONFIG_SET_BINARY, key, &value[0], len(value))
|
||||
}
|
||||
|
||||
// FsconfigSetPath is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_SET_PATH.
|
||||
//
|
||||
// fd is the filesystem context to act upon.
|
||||
// key the parameter key to set.
|
||||
// path is a non-empty path for specified key.
|
||||
// atfd is a file descriptor at which to start lookup from or AT_FDCWD.
|
||||
func FsconfigSetPath(fd int, key string, path string, atfd int) (err error) {
|
||||
var valuep *byte
|
||||
if valuep, err = BytePtrFromString(path); err != nil {
|
||||
return
|
||||
}
|
||||
return fsconfigCommon(fd, FSCONFIG_SET_PATH, key, valuep, atfd)
|
||||
}
|
||||
|
||||
// FsconfigSetPathEmpty is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_SET_PATH_EMPTY. The same as
|
||||
// FconfigSetPath but with AT_PATH_EMPTY implied.
|
||||
func FsconfigSetPathEmpty(fd int, key string, path string, atfd int) (err error) {
|
||||
var valuep *byte
|
||||
if valuep, err = BytePtrFromString(path); err != nil {
|
||||
return
|
||||
}
|
||||
return fsconfigCommon(fd, FSCONFIG_SET_PATH_EMPTY, key, valuep, atfd)
|
||||
}
|
||||
|
||||
// FsconfigSetFd is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_SET_FD.
|
||||
//
|
||||
// fd is the filesystem context to act upon.
|
||||
// key the parameter key to set.
|
||||
// value is a file descriptor to be assigned to specified key.
|
||||
func FsconfigSetFd(fd int, key string, value int) (err error) {
|
||||
return fsconfigCommon(fd, FSCONFIG_SET_FD, key, nil, value)
|
||||
}
|
||||
|
||||
// FsconfigCreate is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_CMD_CREATE.
|
||||
//
|
||||
// fd is the filesystem context to act upon.
|
||||
func FsconfigCreate(fd int) (err error) {
|
||||
return fsconfig(fd, FSCONFIG_CMD_CREATE, nil, nil, 0)
|
||||
}
|
||||
|
||||
// FsconfigReconfigure is equivalent to fsconfig(2) called
|
||||
// with cmd == FSCONFIG_CMD_RECONFIGURE.
|
||||
//
|
||||
// fd is the filesystem context to act upon.
|
||||
func FsconfigReconfigure(fd int) (err error) {
|
||||
return fsconfig(fd, FSCONFIG_CMD_RECONFIGURE, nil, nil, 0)
|
||||
}
|
||||
|
||||
//sys Getdents(fd int, buf []byte) (n int, err error) = SYS_GETDENTS64
|
||||
//sysnb Getpgid(pid int) (pgid int, err error)
|
||||
|
||||
|
|
|
|||
|
|
@ -166,6 +166,20 @@ func Getresgid() (rgid, egid, sgid int) {
|
|||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
//sys fcntl(fd int, cmd int, arg int) (n int, err error)
|
||||
//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) = SYS_FCNTL
|
||||
|
||||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
return fcntl(int(fd), cmd, arg)
|
||||
}
|
||||
|
||||
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
|
||||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
|
||||
_, err := fcntlPtr(int(fd), cmd, unsafe.Pointer(lk))
|
||||
return err
|
||||
}
|
||||
|
||||
//sys ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error)
|
||||
|
||||
func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf[:vallen-1]), nil
|
||||
return ByteSliceToString(buf[:vallen]), nil
|
||||
}
|
||||
|
||||
const ImplementsGetwd = true
|
||||
|
|
|
|||
|
|
@ -1104,7 +1104,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
return string(buf[:vallen-1]), nil
|
||||
return ByteSliceToString(buf[:vallen]), nil
|
||||
}
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
|
|
|
|||
|
|
@ -486,7 +486,6 @@ const (
|
|||
BPF_F_ANY_ALIGNMENT = 0x2
|
||||
BPF_F_BEFORE = 0x8
|
||||
BPF_F_ID = 0x20
|
||||
BPF_F_LINK = 0x2000
|
||||
BPF_F_NETFILTER_IP_DEFRAG = 0x1
|
||||
BPF_F_QUERY_EFFECTIVE = 0x1
|
||||
BPF_F_REPLACE = 0x4
|
||||
|
|
@ -1786,6 +1785,8 @@ const (
|
|||
LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20
|
||||
LANDLOCK_ACCESS_FS_TRUNCATE = 0x4000
|
||||
LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2
|
||||
LANDLOCK_ACCESS_NET_BIND_TCP = 0x1
|
||||
LANDLOCK_ACCESS_NET_CONNECT_TCP = 0x2
|
||||
LANDLOCK_CREATE_RULESET_VERSION = 0x1
|
||||
LINUX_REBOOT_CMD_CAD_OFF = 0x0
|
||||
LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef
|
||||
|
|
@ -1802,6 +1803,7 @@ const (
|
|||
LOCK_SH = 0x1
|
||||
LOCK_UN = 0x8
|
||||
LOOP_CLR_FD = 0x4c01
|
||||
LOOP_CONFIGURE = 0x4c0a
|
||||
LOOP_CTL_ADD = 0x4c80
|
||||
LOOP_CTL_GET_FREE = 0x4c82
|
||||
LOOP_CTL_REMOVE = 0x4c81
|
||||
|
|
@ -2127,6 +2129,60 @@ const (
|
|||
NFNL_SUBSYS_QUEUE = 0x3
|
||||
NFNL_SUBSYS_ULOG = 0x4
|
||||
NFS_SUPER_MAGIC = 0x6969
|
||||
NFT_CHAIN_FLAGS = 0x7
|
||||
NFT_CHAIN_MAXNAMELEN = 0x100
|
||||
NFT_CT_MAX = 0x17
|
||||
NFT_DATA_RESERVED_MASK = 0xffffff00
|
||||
NFT_DATA_VALUE_MAXLEN = 0x40
|
||||
NFT_EXTHDR_OP_MAX = 0x4
|
||||
NFT_FIB_RESULT_MAX = 0x3
|
||||
NFT_INNER_MASK = 0xf
|
||||
NFT_LOGLEVEL_MAX = 0x8
|
||||
NFT_NAME_MAXLEN = 0x100
|
||||
NFT_NG_MAX = 0x1
|
||||
NFT_OBJECT_CONNLIMIT = 0x5
|
||||
NFT_OBJECT_COUNTER = 0x1
|
||||
NFT_OBJECT_CT_EXPECT = 0x9
|
||||
NFT_OBJECT_CT_HELPER = 0x3
|
||||
NFT_OBJECT_CT_TIMEOUT = 0x7
|
||||
NFT_OBJECT_LIMIT = 0x4
|
||||
NFT_OBJECT_MAX = 0xa
|
||||
NFT_OBJECT_QUOTA = 0x2
|
||||
NFT_OBJECT_SECMARK = 0x8
|
||||
NFT_OBJECT_SYNPROXY = 0xa
|
||||
NFT_OBJECT_TUNNEL = 0x6
|
||||
NFT_OBJECT_UNSPEC = 0x0
|
||||
NFT_OBJ_MAXNAMELEN = 0x100
|
||||
NFT_OSF_MAXGENRELEN = 0x10
|
||||
NFT_QUEUE_FLAG_BYPASS = 0x1
|
||||
NFT_QUEUE_FLAG_CPU_FANOUT = 0x2
|
||||
NFT_QUEUE_FLAG_MASK = 0x3
|
||||
NFT_REG32_COUNT = 0x10
|
||||
NFT_REG32_SIZE = 0x4
|
||||
NFT_REG_MAX = 0x4
|
||||
NFT_REG_SIZE = 0x10
|
||||
NFT_REJECT_ICMPX_MAX = 0x3
|
||||
NFT_RT_MAX = 0x4
|
||||
NFT_SECMARK_CTX_MAXLEN = 0x100
|
||||
NFT_SET_MAXNAMELEN = 0x100
|
||||
NFT_SOCKET_MAX = 0x3
|
||||
NFT_TABLE_F_MASK = 0x3
|
||||
NFT_TABLE_MAXNAMELEN = 0x100
|
||||
NFT_TRACETYPE_MAX = 0x3
|
||||
NFT_TUNNEL_F_MASK = 0x7
|
||||
NFT_TUNNEL_MAX = 0x1
|
||||
NFT_TUNNEL_MODE_MAX = 0x2
|
||||
NFT_USERDATA_MAXLEN = 0x100
|
||||
NFT_XFRM_KEY_MAX = 0x6
|
||||
NF_NAT_RANGE_MAP_IPS = 0x1
|
||||
NF_NAT_RANGE_MASK = 0x7f
|
||||
NF_NAT_RANGE_NETMAP = 0x40
|
||||
NF_NAT_RANGE_PERSISTENT = 0x8
|
||||
NF_NAT_RANGE_PROTO_OFFSET = 0x20
|
||||
NF_NAT_RANGE_PROTO_RANDOM = 0x4
|
||||
NF_NAT_RANGE_PROTO_RANDOM_ALL = 0x14
|
||||
NF_NAT_RANGE_PROTO_RANDOM_FULLY = 0x10
|
||||
NF_NAT_RANGE_PROTO_SPECIFIED = 0x2
|
||||
NILFS_SUPER_MAGIC = 0x3434
|
||||
NL0 = 0x0
|
||||
NL1 = 0x100
|
||||
|
|
@ -2411,6 +2467,7 @@ const (
|
|||
PR_MCE_KILL_GET = 0x22
|
||||
PR_MCE_KILL_LATE = 0x0
|
||||
PR_MCE_KILL_SET = 0x1
|
||||
PR_MDWE_NO_INHERIT = 0x2
|
||||
PR_MDWE_REFUSE_EXEC_GAIN = 0x1
|
||||
PR_MPX_DISABLE_MANAGEMENT = 0x2c
|
||||
PR_MPX_ENABLE_MANAGEMENT = 0x2b
|
||||
|
|
@ -2615,8 +2672,9 @@ const (
|
|||
RTAX_FEATURES = 0xc
|
||||
RTAX_FEATURE_ALLFRAG = 0x8
|
||||
RTAX_FEATURE_ECN = 0x1
|
||||
RTAX_FEATURE_MASK = 0xf
|
||||
RTAX_FEATURE_MASK = 0x1f
|
||||
RTAX_FEATURE_SACK = 0x2
|
||||
RTAX_FEATURE_TCP_USEC_TS = 0x10
|
||||
RTAX_FEATURE_TIMESTAMP = 0x4
|
||||
RTAX_HOPLIMIT = 0xa
|
||||
RTAX_INITCWND = 0xb
|
||||
|
|
@ -2859,9 +2917,38 @@ const (
|
|||
SCM_RIGHTS = 0x1
|
||||
SCM_TIMESTAMP = 0x1d
|
||||
SC_LOG_FLUSH = 0x100000
|
||||
SECCOMP_ADDFD_FLAG_SEND = 0x2
|
||||
SECCOMP_ADDFD_FLAG_SETFD = 0x1
|
||||
SECCOMP_FILTER_FLAG_LOG = 0x2
|
||||
SECCOMP_FILTER_FLAG_NEW_LISTENER = 0x8
|
||||
SECCOMP_FILTER_FLAG_SPEC_ALLOW = 0x4
|
||||
SECCOMP_FILTER_FLAG_TSYNC = 0x1
|
||||
SECCOMP_FILTER_FLAG_TSYNC_ESRCH = 0x10
|
||||
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV = 0x20
|
||||
SECCOMP_GET_ACTION_AVAIL = 0x2
|
||||
SECCOMP_GET_NOTIF_SIZES = 0x3
|
||||
SECCOMP_IOCTL_NOTIF_RECV = 0xc0502100
|
||||
SECCOMP_IOCTL_NOTIF_SEND = 0xc0182101
|
||||
SECCOMP_IOC_MAGIC = '!'
|
||||
SECCOMP_MODE_DISABLED = 0x0
|
||||
SECCOMP_MODE_FILTER = 0x2
|
||||
SECCOMP_MODE_STRICT = 0x1
|
||||
SECCOMP_RET_ACTION = 0x7fff0000
|
||||
SECCOMP_RET_ACTION_FULL = 0xffff0000
|
||||
SECCOMP_RET_ALLOW = 0x7fff0000
|
||||
SECCOMP_RET_DATA = 0xffff
|
||||
SECCOMP_RET_ERRNO = 0x50000
|
||||
SECCOMP_RET_KILL = 0x0
|
||||
SECCOMP_RET_KILL_PROCESS = 0x80000000
|
||||
SECCOMP_RET_KILL_THREAD = 0x0
|
||||
SECCOMP_RET_LOG = 0x7ffc0000
|
||||
SECCOMP_RET_TRACE = 0x7ff00000
|
||||
SECCOMP_RET_TRAP = 0x30000
|
||||
SECCOMP_RET_USER_NOTIF = 0x7fc00000
|
||||
SECCOMP_SET_MODE_FILTER = 0x1
|
||||
SECCOMP_SET_MODE_STRICT = 0x0
|
||||
SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP = 0x1
|
||||
SECCOMP_USER_NOTIF_FLAG_CONTINUE = 0x1
|
||||
SECRETMEM_MAGIC = 0x5345434d
|
||||
SECURITYFS_MAGIC = 0x73636673
|
||||
SEEK_CUR = 0x1
|
||||
|
|
@ -3021,6 +3108,7 @@ const (
|
|||
SOL_TIPC = 0x10f
|
||||
SOL_TLS = 0x11a
|
||||
SOL_UDP = 0x11
|
||||
SOL_VSOCK = 0x11f
|
||||
SOL_X25 = 0x106
|
||||
SOL_XDP = 0x11b
|
||||
SOMAXCONN = 0x1000
|
||||
|
|
|
|||
|
|
@ -281,6 +281,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -282,6 +282,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -288,6 +288,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -278,6 +278,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -275,6 +275,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -281,6 +281,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
|
|
|||
|
|
@ -281,6 +281,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
|
|
|||
|
|
@ -281,6 +281,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
|
|
|||
|
|
@ -281,6 +281,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
|
|
|||
|
|
@ -336,6 +336,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -340,6 +340,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -340,6 +340,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -272,6 +272,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -344,6 +344,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
|
|
|||
|
|
@ -335,6 +335,9 @@ const (
|
|||
SCM_TIMESTAMPNS = 0x21
|
||||
SCM_TXTIME = 0x3f
|
||||
SCM_WIFI_STATUS = 0x25
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x400000
|
||||
SFD_NONBLOCK = 0x4000
|
||||
SF_FP = 0x38
|
||||
|
|
|
|||
|
|
@ -37,6 +37,21 @@ func fchmodat(dirfd int, path string, mode uint32) (err error) {
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _, e1 := Syscall6(SYS_FCHMODAT2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ioctl(fd int, req uint, arg uintptr) (err error) {
|
||||
_, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
|
||||
if e1 != 0 {
|
||||
|
|
@ -891,6 +906,16 @@ func Fspick(dirfd int, pathName string, flags int) (fd int, err error) {
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FSCONFIG, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(value)), uintptr(aux), 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Getdents(fd int, buf []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
|
|
|
|||
|
|
@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fcntl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
|
||||
n = int(r0)
|
||||
|
|
@ -2271,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) {
|
|||
var libc_unveil_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_unveil unveil "libc.so"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
|||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_ppoll(SB)
|
||||
GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4
|
||||
|
|
|
|||
|
|
@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fcntl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
|
||||
n = int(r0)
|
||||
|
|
@ -2271,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) {
|
|||
var libc_unveil_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_unveil unveil "libc.so"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
|||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_ppoll(SB)
|
||||
GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8
|
||||
|
|
|
|||
|
|
@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fcntl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
|
||||
n = int(r0)
|
||||
|
|
@ -2271,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) {
|
|||
var libc_unveil_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_unveil unveil "libc.so"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
|||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_ppoll(SB)
|
||||
GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4
|
||||
|
|
|
|||
|
|
@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fcntl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
|
||||
n = int(r0)
|
||||
|
|
@ -2271,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) {
|
|||
var libc_unveil_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_unveil unveil "libc.so"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
|||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_ppoll(SB)
|
||||
GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8
|
||||
|
|
|
|||
|
|
@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fcntl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
|
||||
n = int(r0)
|
||||
|
|
@ -2271,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) {
|
|||
var libc_unveil_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_unveil unveil "libc.so"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
|||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_ppoll(SB)
|
||||
GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8
|
||||
|
|
|
|||
|
|
@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fcntl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
|
||||
n = int(r0)
|
||||
|
|
@ -2271,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) {
|
|||
var libc_unveil_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_unveil unveil "libc.so"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -213,6 +213,12 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
|||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
CALL libc_fcntl(SB)
|
||||
RET
|
||||
GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
|
||||
CALL libc_ppoll(SB)
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fcntl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
|
||||
n = int(r0)
|
||||
|
|
@ -2271,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) {
|
|||
var libc_unveil_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_unveil unveil "libc.so"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
|||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
|
||||
|
||||
TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_ppoll(SB)
|
||||
GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8
|
||||
|
|
|
|||
|
|
@ -448,4 +448,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -371,4 +371,7 @@ const (
|
|||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -412,4 +412,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -315,4 +315,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -309,4 +309,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -432,4 +432,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 4450
|
||||
SYS_CACHESTAT = 4451
|
||||
SYS_FCHMODAT2 = 4452
|
||||
SYS_MAP_SHADOW_STACK = 4453
|
||||
SYS_FUTEX_WAKE = 4454
|
||||
SYS_FUTEX_WAIT = 4455
|
||||
SYS_FUTEX_REQUEUE = 4456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -362,4 +362,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 5450
|
||||
SYS_CACHESTAT = 5451
|
||||
SYS_FCHMODAT2 = 5452
|
||||
SYS_MAP_SHADOW_STACK = 5453
|
||||
SYS_FUTEX_WAKE = 5454
|
||||
SYS_FUTEX_WAIT = 5455
|
||||
SYS_FUTEX_REQUEUE = 5456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -362,4 +362,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 5450
|
||||
SYS_CACHESTAT = 5451
|
||||
SYS_FCHMODAT2 = 5452
|
||||
SYS_MAP_SHADOW_STACK = 5453
|
||||
SYS_FUTEX_WAKE = 5454
|
||||
SYS_FUTEX_WAIT = 5455
|
||||
SYS_FUTEX_REQUEUE = 5456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -432,4 +432,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 4450
|
||||
SYS_CACHESTAT = 4451
|
||||
SYS_FCHMODAT2 = 4452
|
||||
SYS_MAP_SHADOW_STACK = 4453
|
||||
SYS_FUTEX_WAKE = 4454
|
||||
SYS_FUTEX_WAIT = 4455
|
||||
SYS_FUTEX_REQUEUE = 4456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -439,4 +439,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -411,4 +411,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -411,4 +411,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -316,4 +316,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -377,4 +377,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -390,4 +390,8 @@ const (
|
|||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
SYS_CACHESTAT = 451
|
||||
SYS_FCHMODAT2 = 452
|
||||
SYS_MAP_SHADOW_STACK = 453
|
||||
SYS_FUTEX_WAKE = 454
|
||||
SYS_FUTEX_WAIT = 455
|
||||
SYS_FUTEX_REQUEUE = 456
|
||||
)
|
||||
|
|
|
|||
|
|
@ -174,7 +174,8 @@ type FscryptPolicyV2 struct {
|
|||
Contents_encryption_mode uint8
|
||||
Filenames_encryption_mode uint8
|
||||
Flags uint8
|
||||
_ [4]uint8
|
||||
Log2_data_unit_size uint8
|
||||
_ [3]uint8
|
||||
Master_key_identifier [16]uint8
|
||||
}
|
||||
|
||||
|
|
@ -455,60 +456,63 @@ type Ucred struct {
|
|||
}
|
||||
|
||||
type TCPInfo struct {
|
||||
State uint8
|
||||
Ca_state uint8
|
||||
Retransmits uint8
|
||||
Probes uint8
|
||||
Backoff uint8
|
||||
Options uint8
|
||||
Rto uint32
|
||||
Ato uint32
|
||||
Snd_mss uint32
|
||||
Rcv_mss uint32
|
||||
Unacked uint32
|
||||
Sacked uint32
|
||||
Lost uint32
|
||||
Retrans uint32
|
||||
Fackets uint32
|
||||
Last_data_sent uint32
|
||||
Last_ack_sent uint32
|
||||
Last_data_recv uint32
|
||||
Last_ack_recv uint32
|
||||
Pmtu uint32
|
||||
Rcv_ssthresh uint32
|
||||
Rtt uint32
|
||||
Rttvar uint32
|
||||
Snd_ssthresh uint32
|
||||
Snd_cwnd uint32
|
||||
Advmss uint32
|
||||
Reordering uint32
|
||||
Rcv_rtt uint32
|
||||
Rcv_space uint32
|
||||
Total_retrans uint32
|
||||
Pacing_rate uint64
|
||||
Max_pacing_rate uint64
|
||||
Bytes_acked uint64
|
||||
Bytes_received uint64
|
||||
Segs_out uint32
|
||||
Segs_in uint32
|
||||
Notsent_bytes uint32
|
||||
Min_rtt uint32
|
||||
Data_segs_in uint32
|
||||
Data_segs_out uint32
|
||||
Delivery_rate uint64
|
||||
Busy_time uint64
|
||||
Rwnd_limited uint64
|
||||
Sndbuf_limited uint64
|
||||
Delivered uint32
|
||||
Delivered_ce uint32
|
||||
Bytes_sent uint64
|
||||
Bytes_retrans uint64
|
||||
Dsack_dups uint32
|
||||
Reord_seen uint32
|
||||
Rcv_ooopack uint32
|
||||
Snd_wnd uint32
|
||||
Rcv_wnd uint32
|
||||
Rehash uint32
|
||||
State uint8
|
||||
Ca_state uint8
|
||||
Retransmits uint8
|
||||
Probes uint8
|
||||
Backoff uint8
|
||||
Options uint8
|
||||
Rto uint32
|
||||
Ato uint32
|
||||
Snd_mss uint32
|
||||
Rcv_mss uint32
|
||||
Unacked uint32
|
||||
Sacked uint32
|
||||
Lost uint32
|
||||
Retrans uint32
|
||||
Fackets uint32
|
||||
Last_data_sent uint32
|
||||
Last_ack_sent uint32
|
||||
Last_data_recv uint32
|
||||
Last_ack_recv uint32
|
||||
Pmtu uint32
|
||||
Rcv_ssthresh uint32
|
||||
Rtt uint32
|
||||
Rttvar uint32
|
||||
Snd_ssthresh uint32
|
||||
Snd_cwnd uint32
|
||||
Advmss uint32
|
||||
Reordering uint32
|
||||
Rcv_rtt uint32
|
||||
Rcv_space uint32
|
||||
Total_retrans uint32
|
||||
Pacing_rate uint64
|
||||
Max_pacing_rate uint64
|
||||
Bytes_acked uint64
|
||||
Bytes_received uint64
|
||||
Segs_out uint32
|
||||
Segs_in uint32
|
||||
Notsent_bytes uint32
|
||||
Min_rtt uint32
|
||||
Data_segs_in uint32
|
||||
Data_segs_out uint32
|
||||
Delivery_rate uint64
|
||||
Busy_time uint64
|
||||
Rwnd_limited uint64
|
||||
Sndbuf_limited uint64
|
||||
Delivered uint32
|
||||
Delivered_ce uint32
|
||||
Bytes_sent uint64
|
||||
Bytes_retrans uint64
|
||||
Dsack_dups uint32
|
||||
Reord_seen uint32
|
||||
Rcv_ooopack uint32
|
||||
Snd_wnd uint32
|
||||
Rcv_wnd uint32
|
||||
Rehash uint32
|
||||
Total_rto uint16
|
||||
Total_rto_recoveries uint16
|
||||
Total_rto_time uint32
|
||||
}
|
||||
|
||||
type CanFilter struct {
|
||||
|
|
@ -551,7 +555,7 @@ const (
|
|||
SizeofIPv6MTUInfo = 0x20
|
||||
SizeofICMPv6Filter = 0x20
|
||||
SizeofUcred = 0xc
|
||||
SizeofTCPInfo = 0xf0
|
||||
SizeofTCPInfo = 0xf8
|
||||
SizeofCanFilter = 0x8
|
||||
SizeofTCPRepairOpt = 0x8
|
||||
)
|
||||
|
|
@ -832,6 +836,15 @@ const (
|
|||
FSPICK_EMPTY_PATH = 0x8
|
||||
|
||||
FSMOUNT_CLOEXEC = 0x1
|
||||
|
||||
FSCONFIG_SET_FLAG = 0x0
|
||||
FSCONFIG_SET_STRING = 0x1
|
||||
FSCONFIG_SET_BINARY = 0x2
|
||||
FSCONFIG_SET_PATH = 0x3
|
||||
FSCONFIG_SET_PATH_EMPTY = 0x4
|
||||
FSCONFIG_SET_FD = 0x5
|
||||
FSCONFIG_CMD_CREATE = 0x6
|
||||
FSCONFIG_CMD_RECONFIGURE = 0x7
|
||||
)
|
||||
|
||||
type OpenHow struct {
|
||||
|
|
@ -1546,6 +1559,7 @@ const (
|
|||
IFLA_DEVLINK_PORT = 0x3e
|
||||
IFLA_GSO_IPV4_MAX_SIZE = 0x3f
|
||||
IFLA_GRO_IPV4_MAX_SIZE = 0x40
|
||||
IFLA_DPLL_PIN = 0x41
|
||||
IFLA_PROTO_DOWN_REASON_UNSPEC = 0x0
|
||||
IFLA_PROTO_DOWN_REASON_MASK = 0x1
|
||||
IFLA_PROTO_DOWN_REASON_VALUE = 0x2
|
||||
|
|
@ -1561,6 +1575,7 @@ const (
|
|||
IFLA_INET6_ICMP6STATS = 0x6
|
||||
IFLA_INET6_TOKEN = 0x7
|
||||
IFLA_INET6_ADDR_GEN_MODE = 0x8
|
||||
IFLA_INET6_RA_MTU = 0x9
|
||||
IFLA_BR_UNSPEC = 0x0
|
||||
IFLA_BR_FORWARD_DELAY = 0x1
|
||||
IFLA_BR_HELLO_TIME = 0x2
|
||||
|
|
@ -1608,6 +1623,9 @@ const (
|
|||
IFLA_BR_MCAST_MLD_VERSION = 0x2c
|
||||
IFLA_BR_VLAN_STATS_PER_PORT = 0x2d
|
||||
IFLA_BR_MULTI_BOOLOPT = 0x2e
|
||||
IFLA_BR_MCAST_QUERIER_STATE = 0x2f
|
||||
IFLA_BR_FDB_N_LEARNED = 0x30
|
||||
IFLA_BR_FDB_MAX_LEARNED = 0x31
|
||||
IFLA_BRPORT_UNSPEC = 0x0
|
||||
IFLA_BRPORT_STATE = 0x1
|
||||
IFLA_BRPORT_PRIORITY = 0x2
|
||||
|
|
@ -1645,6 +1663,14 @@ const (
|
|||
IFLA_BRPORT_BACKUP_PORT = 0x22
|
||||
IFLA_BRPORT_MRP_RING_OPEN = 0x23
|
||||
IFLA_BRPORT_MRP_IN_OPEN = 0x24
|
||||
IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT = 0x25
|
||||
IFLA_BRPORT_MCAST_EHT_HOSTS_CNT = 0x26
|
||||
IFLA_BRPORT_LOCKED = 0x27
|
||||
IFLA_BRPORT_MAB = 0x28
|
||||
IFLA_BRPORT_MCAST_N_GROUPS = 0x29
|
||||
IFLA_BRPORT_MCAST_MAX_GROUPS = 0x2a
|
||||
IFLA_BRPORT_NEIGH_VLAN_SUPPRESS = 0x2b
|
||||
IFLA_BRPORT_BACKUP_NHID = 0x2c
|
||||
IFLA_INFO_UNSPEC = 0x0
|
||||
IFLA_INFO_KIND = 0x1
|
||||
IFLA_INFO_DATA = 0x2
|
||||
|
|
@ -1666,6 +1692,9 @@ const (
|
|||
IFLA_MACVLAN_MACADDR = 0x4
|
||||
IFLA_MACVLAN_MACADDR_DATA = 0x5
|
||||
IFLA_MACVLAN_MACADDR_COUNT = 0x6
|
||||
IFLA_MACVLAN_BC_QUEUE_LEN = 0x7
|
||||
IFLA_MACVLAN_BC_QUEUE_LEN_USED = 0x8
|
||||
IFLA_MACVLAN_BC_CUTOFF = 0x9
|
||||
IFLA_VRF_UNSPEC = 0x0
|
||||
IFLA_VRF_TABLE = 0x1
|
||||
IFLA_VRF_PORT_UNSPEC = 0x0
|
||||
|
|
@ -1689,9 +1718,22 @@ const (
|
|||
IFLA_XFRM_UNSPEC = 0x0
|
||||
IFLA_XFRM_LINK = 0x1
|
||||
IFLA_XFRM_IF_ID = 0x2
|
||||
IFLA_XFRM_COLLECT_METADATA = 0x3
|
||||
IFLA_IPVLAN_UNSPEC = 0x0
|
||||
IFLA_IPVLAN_MODE = 0x1
|
||||
IFLA_IPVLAN_FLAGS = 0x2
|
||||
NETKIT_NEXT = -0x1
|
||||
NETKIT_PASS = 0x0
|
||||
NETKIT_DROP = 0x2
|
||||
NETKIT_REDIRECT = 0x7
|
||||
NETKIT_L2 = 0x0
|
||||
NETKIT_L3 = 0x1
|
||||
IFLA_NETKIT_UNSPEC = 0x0
|
||||
IFLA_NETKIT_PEER_INFO = 0x1
|
||||
IFLA_NETKIT_PRIMARY = 0x2
|
||||
IFLA_NETKIT_POLICY = 0x3
|
||||
IFLA_NETKIT_PEER_POLICY = 0x4
|
||||
IFLA_NETKIT_MODE = 0x5
|
||||
IFLA_VXLAN_UNSPEC = 0x0
|
||||
IFLA_VXLAN_ID = 0x1
|
||||
IFLA_VXLAN_GROUP = 0x2
|
||||
|
|
@ -1722,6 +1764,8 @@ const (
|
|||
IFLA_VXLAN_GPE = 0x1b
|
||||
IFLA_VXLAN_TTL_INHERIT = 0x1c
|
||||
IFLA_VXLAN_DF = 0x1d
|
||||
IFLA_VXLAN_VNIFILTER = 0x1e
|
||||
IFLA_VXLAN_LOCALBYPASS = 0x1f
|
||||
IFLA_GENEVE_UNSPEC = 0x0
|
||||
IFLA_GENEVE_ID = 0x1
|
||||
IFLA_GENEVE_REMOTE = 0x2
|
||||
|
|
@ -1736,6 +1780,7 @@ const (
|
|||
IFLA_GENEVE_LABEL = 0xb
|
||||
IFLA_GENEVE_TTL_INHERIT = 0xc
|
||||
IFLA_GENEVE_DF = 0xd
|
||||
IFLA_GENEVE_INNER_PROTO_INHERIT = 0xe
|
||||
IFLA_BAREUDP_UNSPEC = 0x0
|
||||
IFLA_BAREUDP_PORT = 0x1
|
||||
IFLA_BAREUDP_ETHERTYPE = 0x2
|
||||
|
|
@ -1748,6 +1793,8 @@ const (
|
|||
IFLA_GTP_FD1 = 0x2
|
||||
IFLA_GTP_PDP_HASHSIZE = 0x3
|
||||
IFLA_GTP_ROLE = 0x4
|
||||
IFLA_GTP_CREATE_SOCKETS = 0x5
|
||||
IFLA_GTP_RESTART_COUNT = 0x6
|
||||
IFLA_BOND_UNSPEC = 0x0
|
||||
IFLA_BOND_MODE = 0x1
|
||||
IFLA_BOND_ACTIVE_SLAVE = 0x2
|
||||
|
|
@ -1777,6 +1824,9 @@ const (
|
|||
IFLA_BOND_AD_ACTOR_SYSTEM = 0x1a
|
||||
IFLA_BOND_TLB_DYNAMIC_LB = 0x1b
|
||||
IFLA_BOND_PEER_NOTIF_DELAY = 0x1c
|
||||
IFLA_BOND_AD_LACP_ACTIVE = 0x1d
|
||||
IFLA_BOND_MISSED_MAX = 0x1e
|
||||
IFLA_BOND_NS_IP6_TARGET = 0x1f
|
||||
IFLA_BOND_AD_INFO_UNSPEC = 0x0
|
||||
IFLA_BOND_AD_INFO_AGGREGATOR = 0x1
|
||||
IFLA_BOND_AD_INFO_NUM_PORTS = 0x2
|
||||
|
|
@ -1792,6 +1842,7 @@ const (
|
|||
IFLA_BOND_SLAVE_AD_AGGREGATOR_ID = 0x6
|
||||
IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE = 0x7
|
||||
IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE = 0x8
|
||||
IFLA_BOND_SLAVE_PRIO = 0x9
|
||||
IFLA_VF_INFO_UNSPEC = 0x0
|
||||
IFLA_VF_INFO = 0x1
|
||||
IFLA_VF_UNSPEC = 0x0
|
||||
|
|
@ -1850,8 +1901,16 @@ const (
|
|||
IFLA_STATS_LINK_XSTATS_SLAVE = 0x3
|
||||
IFLA_STATS_LINK_OFFLOAD_XSTATS = 0x4
|
||||
IFLA_STATS_AF_SPEC = 0x5
|
||||
IFLA_STATS_GETSET_UNSPEC = 0x0
|
||||
IFLA_STATS_GET_FILTERS = 0x1
|
||||
IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS = 0x2
|
||||
IFLA_OFFLOAD_XSTATS_UNSPEC = 0x0
|
||||
IFLA_OFFLOAD_XSTATS_CPU_HIT = 0x1
|
||||
IFLA_OFFLOAD_XSTATS_HW_S_INFO = 0x2
|
||||
IFLA_OFFLOAD_XSTATS_L3_STATS = 0x3
|
||||
IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC = 0x0
|
||||
IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST = 0x1
|
||||
IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED = 0x2
|
||||
IFLA_XDP_UNSPEC = 0x0
|
||||
IFLA_XDP_FD = 0x1
|
||||
IFLA_XDP_ATTACHED = 0x2
|
||||
|
|
@ -1881,6 +1940,11 @@ const (
|
|||
IFLA_RMNET_UNSPEC = 0x0
|
||||
IFLA_RMNET_MUX_ID = 0x1
|
||||
IFLA_RMNET_FLAGS = 0x2
|
||||
IFLA_MCTP_UNSPEC = 0x0
|
||||
IFLA_MCTP_NET = 0x1
|
||||
IFLA_DSA_UNSPEC = 0x0
|
||||
IFLA_DSA_CONDUIT = 0x1
|
||||
IFLA_DSA_MASTER = 0x1
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -2671,6 +2735,7 @@ const (
|
|||
BPF_PROG_TYPE_LSM = 0x1d
|
||||
BPF_PROG_TYPE_SK_LOOKUP = 0x1e
|
||||
BPF_PROG_TYPE_SYSCALL = 0x1f
|
||||
BPF_PROG_TYPE_NETFILTER = 0x20
|
||||
BPF_CGROUP_INET_INGRESS = 0x0
|
||||
BPF_CGROUP_INET_EGRESS = 0x1
|
||||
BPF_CGROUP_INET_SOCK_CREATE = 0x2
|
||||
|
|
@ -2715,6 +2780,11 @@ const (
|
|||
BPF_PERF_EVENT = 0x29
|
||||
BPF_TRACE_KPROBE_MULTI = 0x2a
|
||||
BPF_LSM_CGROUP = 0x2b
|
||||
BPF_STRUCT_OPS = 0x2c
|
||||
BPF_NETFILTER = 0x2d
|
||||
BPF_TCX_INGRESS = 0x2e
|
||||
BPF_TCX_EGRESS = 0x2f
|
||||
BPF_TRACE_UPROBE_MULTI = 0x30
|
||||
BPF_LINK_TYPE_UNSPEC = 0x0
|
||||
BPF_LINK_TYPE_RAW_TRACEPOINT = 0x1
|
||||
BPF_LINK_TYPE_TRACING = 0x2
|
||||
|
|
@ -2725,6 +2795,18 @@ const (
|
|||
BPF_LINK_TYPE_PERF_EVENT = 0x7
|
||||
BPF_LINK_TYPE_KPROBE_MULTI = 0x8
|
||||
BPF_LINK_TYPE_STRUCT_OPS = 0x9
|
||||
BPF_LINK_TYPE_NETFILTER = 0xa
|
||||
BPF_LINK_TYPE_TCX = 0xb
|
||||
BPF_LINK_TYPE_UPROBE_MULTI = 0xc
|
||||
BPF_PERF_EVENT_UNSPEC = 0x0
|
||||
BPF_PERF_EVENT_UPROBE = 0x1
|
||||
BPF_PERF_EVENT_URETPROBE = 0x2
|
||||
BPF_PERF_EVENT_KPROBE = 0x3
|
||||
BPF_PERF_EVENT_KRETPROBE = 0x4
|
||||
BPF_PERF_EVENT_TRACEPOINT = 0x5
|
||||
BPF_PERF_EVENT_EVENT = 0x6
|
||||
BPF_F_KPROBE_MULTI_RETURN = 0x1
|
||||
BPF_F_UPROBE_MULTI_RETURN = 0x1
|
||||
BPF_ANY = 0x0
|
||||
BPF_NOEXIST = 0x1
|
||||
BPF_EXIST = 0x2
|
||||
|
|
@ -2742,6 +2824,8 @@ const (
|
|||
BPF_F_MMAPABLE = 0x400
|
||||
BPF_F_PRESERVE_ELEMS = 0x800
|
||||
BPF_F_INNER_MAP = 0x1000
|
||||
BPF_F_LINK = 0x2000
|
||||
BPF_F_PATH_FD = 0x4000
|
||||
BPF_STATS_RUN_TIME = 0x0
|
||||
BPF_STACK_BUILD_ID_EMPTY = 0x0
|
||||
BPF_STACK_BUILD_ID_VALID = 0x1
|
||||
|
|
@ -2762,6 +2846,7 @@ const (
|
|||
BPF_F_ZERO_CSUM_TX = 0x2
|
||||
BPF_F_DONT_FRAGMENT = 0x4
|
||||
BPF_F_SEQ_NUMBER = 0x8
|
||||
BPF_F_NO_TUNNEL_KEY = 0x10
|
||||
BPF_F_TUNINFO_FLAGS = 0x10
|
||||
BPF_F_INDEX_MASK = 0xffffffff
|
||||
BPF_F_CURRENT_CPU = 0xffffffff
|
||||
|
|
@ -2778,6 +2863,8 @@ const (
|
|||
BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10
|
||||
BPF_F_ADJ_ROOM_NO_CSUM_RESET = 0x20
|
||||
BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 0x40
|
||||
BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 0x80
|
||||
BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 0x100
|
||||
BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff
|
||||
BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38
|
||||
BPF_F_SYSCTL_BASE_NAME = 0x1
|
||||
|
|
@ -2866,6 +2953,8 @@ const (
|
|||
BPF_DEVCG_DEV_CHAR = 0x2
|
||||
BPF_FIB_LOOKUP_DIRECT = 0x1
|
||||
BPF_FIB_LOOKUP_OUTPUT = 0x2
|
||||
BPF_FIB_LOOKUP_SKIP_NEIGH = 0x4
|
||||
BPF_FIB_LOOKUP_TBID = 0x8
|
||||
BPF_FIB_LKUP_RET_SUCCESS = 0x0
|
||||
BPF_FIB_LKUP_RET_BLACKHOLE = 0x1
|
||||
BPF_FIB_LKUP_RET_UNREACHABLE = 0x2
|
||||
|
|
@ -2901,6 +2990,7 @@ const (
|
|||
BPF_CORE_ENUMVAL_EXISTS = 0xa
|
||||
BPF_CORE_ENUMVAL_VALUE = 0xb
|
||||
BPF_CORE_TYPE_MATCHES = 0xc
|
||||
BPF_F_TIMER_ABS = 0x1
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -2979,6 +3069,12 @@ type LoopInfo64 struct {
|
|||
Encrypt_key [32]uint8
|
||||
Init [2]uint64
|
||||
}
|
||||
type LoopConfig struct {
|
||||
Fd uint32
|
||||
Size uint32
|
||||
Info LoopInfo64
|
||||
_ [8]uint64
|
||||
}
|
||||
|
||||
type TIPCSocketAddr struct {
|
||||
Ref uint32
|
||||
|
|
@ -3367,7 +3463,7 @@ const (
|
|||
DEVLINK_PORT_FN_ATTR_STATE = 0x2
|
||||
DEVLINK_PORT_FN_ATTR_OPSTATE = 0x3
|
||||
DEVLINK_PORT_FN_ATTR_CAPS = 0x4
|
||||
DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x4
|
||||
DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x5
|
||||
)
|
||||
|
||||
type FsverityDigest struct {
|
||||
|
|
@ -4151,7 +4247,8 @@ const (
|
|||
)
|
||||
|
||||
type LandlockRulesetAttr struct {
|
||||
Access_fs uint64
|
||||
Access_fs uint64
|
||||
Access_net uint64
|
||||
}
|
||||
|
||||
type LandlockPathBeneathAttr struct {
|
||||
|
|
@ -5102,7 +5199,7 @@ const (
|
|||
NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf
|
||||
NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe
|
||||
NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf
|
||||
NL80211_FREQUENCY_ATTR_MAX = 0x1b
|
||||
NL80211_FREQUENCY_ATTR_MAX = 0x1c
|
||||
NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6
|
||||
NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11
|
||||
NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc
|
||||
|
|
@ -5515,7 +5612,7 @@ const (
|
|||
NL80211_REGDOM_TYPE_CUSTOM_WORLD = 0x2
|
||||
NL80211_REGDOM_TYPE_INTERSECTION = 0x3
|
||||
NL80211_REGDOM_TYPE_WORLD = 0x1
|
||||
NL80211_REG_RULE_ATTR_MAX = 0x7
|
||||
NL80211_REG_RULE_ATTR_MAX = 0x8
|
||||
NL80211_REKEY_DATA_AKM = 0x4
|
||||
NL80211_REKEY_DATA_KCK = 0x2
|
||||
NL80211_REKEY_DATA_KEK = 0x1
|
||||
|
|
|
|||
|
|
@ -37,14 +37,17 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) {
|
|||
return nil, err
|
||||
}
|
||||
defer DestroyEnvironmentBlock(block)
|
||||
blockp := unsafe.Pointer(block)
|
||||
for {
|
||||
entry := UTF16PtrToString((*uint16)(blockp))
|
||||
if len(entry) == 0 {
|
||||
break
|
||||
size := unsafe.Sizeof(*block)
|
||||
for *block != 0 {
|
||||
// find NUL terminator
|
||||
end := unsafe.Pointer(block)
|
||||
for *(*uint16)(end) != 0 {
|
||||
end = unsafe.Add(end, size)
|
||||
}
|
||||
env = append(env, entry)
|
||||
blockp = unsafe.Add(blockp, 2*(len(entry)+1))
|
||||
|
||||
entry := unsafe.Slice(block, (uintptr(end)-uintptr(unsafe.Pointer(block)))/size)
|
||||
env = append(env, UTF16ToString(entry))
|
||||
block = (*uint16)(unsafe.Add(end, size))
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,7 @@ func UTF16PtrToString(p *uint16) string {
|
|||
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ {
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p))
|
||||
}
|
||||
|
||||
return string(utf16.Decode(unsafe.Slice(p, n)))
|
||||
return UTF16ToString(unsafe.Slice(p, n))
|
||||
}
|
||||
|
||||
func Getpagesize() int { return 4096 }
|
||||
|
|
@ -155,6 +154,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||
//sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
|
||||
//sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW
|
||||
//sys SetDefaultDllDirectories(directoryFlags uint32) (err error)
|
||||
//sys AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory
|
||||
//sys RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory
|
||||
//sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW
|
||||
//sys GetVersion() (ver uint32, err error)
|
||||
//sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
|
||||
|
|
@ -192,6 +193,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||
//sys GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW
|
||||
//sys GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
|
||||
//sys SetEndOfFile(handle Handle) (err error)
|
||||
//sys SetFileValidData(handle Handle, validDataLength int64) (err error)
|
||||
//sys GetSystemTimeAsFileTime(time *Filetime)
|
||||
//sys GetSystemTimePreciseAsFileTime(time *Filetime)
|
||||
//sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) [failretval==0xffffffff]
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ var (
|
|||
procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
|
||||
procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx")
|
||||
procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
|
||||
procAddDllDirectory = modkernel32.NewProc("AddDllDirectory")
|
||||
procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject")
|
||||
procCancelIo = modkernel32.NewProc("CancelIo")
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
|
|
@ -330,6 +331,7 @@ var (
|
|||
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
||||
procReleaseMutex = modkernel32.NewProc("ReleaseMutex")
|
||||
procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
|
||||
procRemoveDllDirectory = modkernel32.NewProc("RemoveDllDirectory")
|
||||
procResetEvent = modkernel32.NewProc("ResetEvent")
|
||||
procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole")
|
||||
procResumeThread = modkernel32.NewProc("ResumeThread")
|
||||
|
|
@ -340,6 +342,7 @@ var (
|
|||
procSetDefaultDllDirectories = modkernel32.NewProc("SetDefaultDllDirectories")
|
||||
procSetDllDirectoryW = modkernel32.NewProc("SetDllDirectoryW")
|
||||
procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
|
||||
procSetFileValidData = modkernel32.NewProc("SetFileValidData")
|
||||
procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
|
||||
procSetErrorMode = modkernel32.NewProc("SetErrorMode")
|
||||
procSetEvent = modkernel32.NewProc("SetEvent")
|
||||
|
|
@ -1605,6 +1608,15 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) {
|
|||
return
|
||||
}
|
||||
|
||||
func AddDllDirectory(path *uint16) (cookie uintptr, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
|
||||
cookie = uintptr(r0)
|
||||
if cookie == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func AssignProcessToJobObject(job Handle, process Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0)
|
||||
if r1 == 0 {
|
||||
|
|
@ -2879,6 +2891,14 @@ func RemoveDirectory(path *uint16) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func RemoveDllDirectory(cookie uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ResetEvent(event Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0)
|
||||
if r1 == 0 {
|
||||
|
|
@ -2969,6 +2989,14 @@ func SetEndOfFile(handle Handle) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func SetFileValidData(handle Handle, validDataLength int64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0)
|
||||
if r1 == 0 {
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ github.com/containerd/containerd/errdefs
|
|||
## explicit; go 1.19
|
||||
github.com/containerd/stargz-snapshotter/estargz
|
||||
github.com/containerd/stargz-snapshotter/estargz/errorutil
|
||||
# github.com/cyphar/filepath-securejoin v0.2.4
|
||||
## explicit; go 1.13
|
||||
# github.com/cyphar/filepath-securejoin v0.3.6
|
||||
## explicit; go 1.18
|
||||
github.com/cyphar/filepath-securejoin
|
||||
# github.com/davecgh/go-spew v1.1.1
|
||||
## explicit
|
||||
|
|
@ -175,7 +175,7 @@ golang.org/x/mod/semver
|
|||
# golang.org/x/sync v0.4.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/sync/errgroup
|
||||
# golang.org/x/sys v0.14.0
|
||||
# golang.org/x/sys v0.18.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/sys/execabs
|
||||
golang.org/x/sys/unix
|
||||
|
|
|
|||
Loading…
Reference in New Issue