1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

[llvm-objcopy] preserve file ownership when overwritten by root

As of binutils 2.36, GNU strip calls chown(2) for "sudo strip foo" and
"sudo strip foo -o foo", but no "sudo strip foo -o bar" or "sudo strip
foo -o ./foo". In other words, while "sudo strip foo -o bar" creates a
new file bar with root access, "sudo strip foo" will keep the owner and
group of foo unchanged. Currently llvm-objcopy and llvm-strip behave
differently, always changing the owner and gropu to root. The
discrepancy prevents Chrome OS from migrating to llvm-objcopy and
llvm-strip as they change file ownership and cause intended users/groups
to lose access when invoked by sudo with the following sequence
(recommended in man page of GNU strip).

1.<Link the executable as normal.>
1.<Copy "foo" to "foo.full">
1.<Run "strip --strip-debug foo">
1.<Run "objcopy --add-gnu-debuglink=foo.full foo">

This patch makes llvm-objcopy and llvm-strip follow GNU's behavior.

Link: crbug.com/1108880
This commit is contained in:
Jian Cai 2021-02-02 18:47:03 -08:00
parent e9020e2ee2
commit f73c5e8b40
7 changed files with 52 additions and 7 deletions

View File

@ -28,12 +28,15 @@ namespace llvm {
class FileOutputBuffer {
public:
enum {
/// set the 'x' bit on the resulting file
/// Set the 'x' bit on the resulting file.
F_executable = 1,
/// Don't use mmap and instead write an in-memory buffer to a file when this
/// buffer is closed.
F_no_mmap = 2,
/// Preserve ownership if the file already exists.
F_keep_ownership = 4,
};
/// Factory method to create an OutputBuffer object which manages a read/write
@ -46,7 +49,8 @@ public:
/// \p Size. It is an error to specify F_modify and Size=-1 if \p FilePath
/// does not exist.
static Expected<std::unique_ptr<FileOutputBuffer>>
create(StringRef FilePath, size_t Size, unsigned Flags = 0);
create(StringRef FilePath, size_t Size, unsigned Flags = 0,
unsigned UserID = 0, unsigned GroupID = 0);
/// Returns a pointer to the start of the buffer.
virtual uint8_t *getBufferStart() const = 0;

View File

@ -1159,6 +1159,16 @@ std::error_code unlockFile(int FD);
/// means that the filesystem may have failed to perform some buffered writes.
std::error_code closeFile(file_t &F);
#ifdef LLVM_ON_UNIX
/// @brief Change ownership of a file.
///
/// @param Owner The owner of the file to change to.
/// @param Group The group of the file to change to.
/// @returns errc::success if successfully updated file ownership, otherwise an
/// error code is returned.
std::error_code changeFileOwnership(int FD, uint32_t Owner, uint32_t Group);
#endif
/// RAII class that facilitates file locking.
class FileLocker {
int FD; ///< Locked file handle.

View File

@ -125,7 +125,8 @@ createInMemoryBuffer(StringRef Path, size_t Size, unsigned Mode) {
}
static Expected<std::unique_ptr<FileOutputBuffer>>
createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode) {
createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode,
bool KeepOwnership, unsigned UserID, unsigned GroupID) {
Expected<fs::TempFile> FileOrErr =
fs::TempFile::create(Path + ".tmp%%%%%%%", Mode);
if (!FileOrErr)
@ -133,6 +134,13 @@ createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode) {
fs::TempFile File = std::move(*FileOrErr);
#ifndef _WIN32
// Try to preserve file ownership if requested.
if (KeepOwnership) {
fs::file_status Stat;
if (!fs::status(File.FD, Stat) && Stat.getUser() == 0)
fs::changeFileOwnership(File.FD, UserID, GroupID);
}
// On Windows, CreateFileMapping (the mmap function on Windows)
// automatically extends the underlying file. We don't need to
// extend the file beforehand. _chsize (ftruncate on Windows) is
@ -163,7 +171,8 @@ createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode) {
// Create an instance of FileOutputBuffer.
Expected<std::unique_ptr<FileOutputBuffer>>
FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) {
FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags,
unsigned UserID, unsigned GroupID) {
// Handle "-" as stdout just like llvm::raw_ostream does.
if (Path == "-")
return createInMemoryBuffer("-", Size, /*Mode=*/0);
@ -196,7 +205,8 @@ FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) {
if (Flags & F_no_mmap)
return createInMemoryBuffer(Path, Size, Mode);
else
return createOnDiskBuffer(Path, Size, Mode);
return createOnDiskBuffer(Path, Size, Mode, Flags & F_keep_ownership,
UserID, GroupID);
default:
return createInMemoryBuffer(Path, Size, Mode);
}

View File

@ -1211,6 +1211,14 @@ std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest,
return std::error_code();
}
std::error_code changeFileOwnership(int FD, uint32_t Owner, uint32_t Group) {
auto FChown = [&]() { return ::fchown(FD, Owner, Group); };
// Retry if fchown call fails due to interruption.
if ((sys::RetryAfterSignal(-1, FChown)) < 0)
return std::error_code(errno, std::generic_category());
return std::error_code();
}
} // end namespace fs
namespace path {

View File

@ -36,7 +36,12 @@ Error FileBuffer::allocate(size_t Size) {
}
Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable);
FileOutputBuffer::create(getName(), Size,
KeepOwnership
? FileOutputBuffer::F_executable |
FileOutputBuffer::F_keep_ownership
: FileOutputBuffer::F_executable,
UserID, GroupID);
// FileOutputBuffer::create() returns an Error that is just a wrapper around
// std::error_code. Wrap it in FileError to include the actual filename.
if (!BufferOrErr)

View File

@ -40,6 +40,9 @@ class FileBuffer : public Buffer {
// Indicates that allocate(0) was called, and commit() should create or
// truncate a file instead of using a FileOutputBuffer.
bool EmptyFile = false;
bool KeepOwnership = false;
unsigned UserID = 0;
unsigned GroupID = 0;
public:
Error allocate(size_t Size) override;
@ -47,6 +50,8 @@ public:
Error commit() override;
explicit FileBuffer(StringRef FileName) : Buffer(FileName) {}
explicit FileBuffer(StringRef FileName, bool Keep, unsigned UID, unsigned GID)
: Buffer(FileName), KeepOwnership(Keep), UserID(UID), GroupID(GID) {}
};
class MemBuffer : public Buffer {

View File

@ -310,7 +310,10 @@ static Error executeObjcopy(CopyConfig &Config) {
if (Error E = executeObjcopyOnArchive(Config, *Ar))
return E;
} else {
FileBuffer FB(Config.OutputFilename);
FileBuffer FB(Config.OutputFilename,
Config.InputFilename != "-" &&
Config.InputFilename == Config.OutputFilename,
Stat.getUser(), Stat.getGroup());
if (Error E = executeObjcopyOnBinary(Config,
*BinaryOrErr.get().getBinary(), FB))
return E;