зеркало из https://github.com/microsoft/BuildXL.git
Merged PR 782444: Refactor Linux sandbox to use new Create/ReportAccess functions in BxlObserver
Reinstate the change reverted by !781882 after the fixes in !782414
This commit is contained in:
Родитель
ea58784fe6
Коммит
027a61067f
|
@ -634,26 +634,26 @@ namespace Test.BuildXL.Processes
|
|||
public void CallTestmkdir()
|
||||
{
|
||||
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
|
||||
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testdirectory")));
|
||||
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testdirectory")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CallTestmkdirat()
|
||||
{
|
||||
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
|
||||
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testdirectory")));
|
||||
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testdirectory")));
|
||||
}
|
||||
|
||||
public void CallTestmknod()
|
||||
{
|
||||
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
|
||||
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testfile")));
|
||||
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testfile")));
|
||||
}
|
||||
|
||||
public void CallTestmknodat()
|
||||
{
|
||||
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
|
||||
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testfile")));
|
||||
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testfile")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -659,9 +659,20 @@ void BxlObserver::report_exec(const char *syscallName, const char *procName, con
|
|||
{
|
||||
if (IsMonitoringChildProcesses())
|
||||
{
|
||||
// first report 'procName' as is (without trying to resolve it) to ensure that a process name is reported before anything else
|
||||
report_access(syscallName, ES_EVENT_TYPE_NOTIFY_EXEC, procName, empty_str_, mode, error, /* checkCache */ true, associatedPid);
|
||||
report_access(syscallName, ES_EVENT_TYPE_NOTIFY_EXEC, file, mode, /*flags*/ 0, error, /* checkCache */ true, associatedPid);
|
||||
if (associatedPid == 0)
|
||||
{
|
||||
associatedPid = getpid();
|
||||
}
|
||||
|
||||
auto event = buildxl::linux::SandboxEvent::AbsolutePathSandboxEvent(
|
||||
/* event_type */ ES_EVENT_TYPE_NOTIFY_EXEC,
|
||||
/* pid */ associatedPid,
|
||||
/* error */ error,
|
||||
/* src_path */ file);
|
||||
event.SetMode(mode);
|
||||
|
||||
CreateAndReportAccess(syscallName, event);
|
||||
report_exec_args(associatedPid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -728,249 +739,12 @@ void BxlObserver::report_exec_args(pid_t pid, const char *args) {
|
|||
}
|
||||
}
|
||||
|
||||
void BxlObserver::report_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, mode_t mode, int error, bool checkCache, pid_t associatedPid)
|
||||
{
|
||||
if (reportPath == nullptr || secondPath == nullptr)
|
||||
{
|
||||
// If we're given null paths, we can't really build a report. Just return.
|
||||
LOG_DEBUG("Can't report an access for syscall %s with a null path. reportPath = %p, secondPath %p", syscallName, reportPath, secondPath);
|
||||
return;
|
||||
}
|
||||
|
||||
report_access_internal(syscallName, eventType, reportPath, secondPath, mode, error, checkCache, associatedPid);
|
||||
}
|
||||
|
||||
void BxlObserver::report_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, mode_t mode, int error, bool checkCache, pid_t associatedPid)
|
||||
{
|
||||
AccessReportGroup report;
|
||||
create_access_internal(syscallName, eventType, reportPath, secondPath, report, mode, checkCache, associatedPid);
|
||||
report.SetErrno(error);
|
||||
SendReport(report);
|
||||
}
|
||||
|
||||
AccessCheckResult BxlObserver::create_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode, bool checkCache, pid_t associatedPid)
|
||||
{
|
||||
if (reportPath == nullptr || secondPath == nullptr)
|
||||
{
|
||||
LOG_DEBUG("Can't create an access for syscall %s with a null path. reportPath = %p, secondPath %p", syscallName, reportPath, secondPath);
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
return create_access_internal(syscallName, eventType, reportPath, secondPath, reportGroup, mode, checkCache, associatedPid);
|
||||
}
|
||||
|
||||
AccessCheckResult BxlObserver::create_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode, bool checkCache, pid_t associatedPid)
|
||||
{
|
||||
secondPath = secondPath == nullptr ? empty_str_ : secondPath;
|
||||
if (checkCache && IsCacheHit(eventType, std::move(std::string(reportPath)), std::move(std::string(secondPath))))
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
if (mode == 0)
|
||||
{
|
||||
// Mode hasn't been computed yet. Let's do it here.
|
||||
mode = get_mode(reportPath);
|
||||
}
|
||||
|
||||
// If this file descriptor is a non-file (e.g., a pipe, or socket, etc.) then we don't care about it
|
||||
if (is_non_file(mode))
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
std::string execPath = eventType == ES_EVENT_TYPE_NOTIFY_EXEC
|
||||
? std::string(reportPath)
|
||||
: std::string(progFullPath_);
|
||||
|
||||
IOEvent event(associatedPid == 0 ? getpid() : associatedPid, 0, getppid(), eventType, ES_ACTION_TYPE_NOTIFY, std::move(std::string(reportPath)), std::move(std::string(secondPath)), std::move(execPath), mode, false);
|
||||
return create_access(syscallName, event, reportGroup, /* checkCache */ false /* because already checked cache above */);
|
||||
}
|
||||
|
||||
void BxlObserver::report_access(const char *syscallName, IOEvent &event, bool checkCache)
|
||||
{
|
||||
AccessReportGroup report;
|
||||
create_access(syscallName, event, report, checkCache);
|
||||
SendReport(report);
|
||||
}
|
||||
|
||||
AccessCheckResult BxlObserver::create_access(const char *syscallName, IOEvent &event, AccessReportGroup &reportGroup, bool checkCache)
|
||||
{
|
||||
es_event_type_t eventType = event.GetEventType();
|
||||
|
||||
if (checkCache && IsCacheHit(eventType, event.GetSrcPath(), event.GetDstPath()))
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
AccessCheckResult result = sNotChecked;
|
||||
pid_t pid = event.GetPid() == 0 ? getpid() : event.GetPid();
|
||||
bool accessShouldBeBlocked = false;
|
||||
|
||||
if (IsEnabled(pid))
|
||||
{
|
||||
IOHandler handler(sandbox_);
|
||||
handler.SetProcess(process_);
|
||||
result = handler.CheckAccessAndBuildReport(event, reportGroup);
|
||||
accessShouldBeBlocked = result.ShouldDenyAccess() && IsFailingUnexpectedAccesses();
|
||||
if (!accessShouldBeBlocked)
|
||||
{
|
||||
// This access won't be blocked, so let's cache it.
|
||||
// We populate cache even if checkCache is false, but this should be ok.
|
||||
// We cache event types that are always a miss in IsCacheHit, but this also shoulld be fine.
|
||||
CheckCache(eventType, event.GetSrcPath(), /* addEntryIfMissing */ true);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("(( %10s:%2d )) %s %s%s", syscallName, event.GetEventType(), event.GetEventPath(),
|
||||
!result.ShouldReport() ? "[Ignored]" : result.ShouldDenyAccess() ? "[Denied]" : "[Allowed]",
|
||||
accessShouldBeBlocked ? "[Blocked]" : "");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void BxlObserver::report_access(const char *syscallName, es_event_type_t eventType, const char *pathname, mode_t mode, int flags, int error, bool checkCache, pid_t associatedPid)
|
||||
{
|
||||
// If the path is null or if we can't normalize it, we have no meaningful way of reporting this access
|
||||
if (pathname == nullptr)
|
||||
{
|
||||
LOG_DEBUG("Can't report an access for syscall %s with a null path.", syscallName);
|
||||
return;
|
||||
}
|
||||
|
||||
auto normalized = normalize_path(pathname, flags, associatedPid);
|
||||
if (normalized.length() == 0)
|
||||
{
|
||||
LOG_DEBUG("Couldn't normalize path %s", pathname);
|
||||
return;
|
||||
}
|
||||
|
||||
report_access_internal(syscallName, eventType, normalized.c_str(), /*secondPath*/ nullptr , mode, error, checkCache, associatedPid);
|
||||
}
|
||||
|
||||
AccessCheckResult BxlObserver::create_access(const char *syscallName, es_event_type_t eventType, const char *pathname, AccessReportGroup &reportGroup, mode_t mode, int flags, bool checkCache, pid_t associatedPid)
|
||||
{
|
||||
// If the path is null or if we can't normalize it, we have no meaningful way of reporting this access
|
||||
if (pathname == nullptr)
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
auto normalized = normalize_path(pathname, flags, associatedPid);
|
||||
if (normalized.length() == 0)
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
return create_access_internal(syscallName, eventType, normalized.c_str(), /* secondPath */ nullptr, reportGroup, mode, checkCache, associatedPid);
|
||||
}
|
||||
|
||||
void BxlObserver::report_access_fd(const char *syscallName, es_event_type_t eventType, int fd, int error, pid_t associatedPid)
|
||||
{
|
||||
AccessReportGroup report;
|
||||
create_access_fd(syscallName, eventType, fd, report, associatedPid);
|
||||
report.SetErrno(error);
|
||||
SendReport(report);
|
||||
}
|
||||
|
||||
AccessCheckResult BxlObserver::create_access_fd(const char *syscallName, es_event_type_t eventType, int fd, AccessReportGroup &report, pid_t associatedPid)
|
||||
{
|
||||
mode_t mode = get_mode(fd);
|
||||
|
||||
// If this file descriptor is a non-file (e.g., a pipe, or socket, etc.) then we don't care about it
|
||||
if (is_non_file(mode))
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
std::string fullpath = fd_to_path(fd, associatedPid);
|
||||
|
||||
// Only reports when fd_to_path succeeded.
|
||||
return fullpath.length() > 0
|
||||
? create_access_internal(syscallName, eventType, fullpath.c_str(), /* secondPath */ nullptr, report, mode, /* checkCache */ true, associatedPid)
|
||||
: sNotChecked;
|
||||
}
|
||||
|
||||
bool BxlObserver::is_non_file(const mode_t mode)
|
||||
{
|
||||
// Observe we don't care about block devices here. It is unlikely that we'll support them e2e, this is just an FYI.
|
||||
return mode != 0 && !S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode);
|
||||
}
|
||||
|
||||
AccessCheckResult BxlObserver::create_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, AccessReportGroup &report, int flags, bool getModeWithFd, pid_t associatedPid)
|
||||
{
|
||||
if (pathname == nullptr)
|
||||
{
|
||||
LOG_DEBUG("Can't create an access for syscall %s with a null path.", syscallName);
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
if (pathname[0] == '/')
|
||||
{
|
||||
return create_access(syscallName, eventType, pathname, report, /* mode */0, flags, /* checkCache */ true, associatedPid);
|
||||
}
|
||||
|
||||
char fullpath[PATH_MAX] = {0};
|
||||
ssize_t len = 0;
|
||||
|
||||
mode_t mode = 0;
|
||||
|
||||
if (dirfd == AT_FDCWD)
|
||||
{
|
||||
if (!getcurrentworkingdirectory(fullpath, PATH_MAX, associatedPid))
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
len = strlen(fullpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string dirPath;
|
||||
|
||||
// If getModeWithFd is set, then we can call get_mode directly with the file descriptor instead of a path
|
||||
// If false, then use the provided associatedPid to convert the fd to a path and the get_mode on the path
|
||||
if (getModeWithFd)
|
||||
{
|
||||
mode = get_mode(dirfd);
|
||||
}
|
||||
else
|
||||
{
|
||||
dirPath = fd_to_path(dirfd, associatedPid);
|
||||
mode = get_mode(dirPath.c_str());
|
||||
}
|
||||
|
||||
// If this file descriptor is a non-file (e.g., a pipe, or socket, etc.) then we don't care about it
|
||||
if (is_non_file(mode))
|
||||
{
|
||||
return sNotChecked;
|
||||
}
|
||||
|
||||
if (dirPath.empty())
|
||||
{
|
||||
dirPath = fd_to_path(dirfd);
|
||||
}
|
||||
|
||||
len = dirPath.length();
|
||||
strcpy(fullpath, dirPath.c_str());
|
||||
}
|
||||
|
||||
if (len <= 0)
|
||||
{
|
||||
_fatal("Could not get path for fd %d; errno: %d", dirfd, errno);
|
||||
}
|
||||
|
||||
snprintf(&fullpath[len], PATH_MAX - len, "/%s", pathname);
|
||||
return create_access(syscallName, eventType, fullpath, report, mode, flags, /* checkCache */ true, associatedPid);
|
||||
}
|
||||
|
||||
void BxlObserver::report_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, int flags, bool getModeWithFd, pid_t associatedPid, int error)
|
||||
{
|
||||
AccessReportGroup report;
|
||||
create_access_at(syscallName, eventType, dirfd, pathname, report, flags, getModeWithFd, associatedPid);
|
||||
report.SetErrno(error);
|
||||
SendReport(report);
|
||||
}
|
||||
|
||||
void BxlObserver::report_firstAllowWriteCheck(const char *fullPath)
|
||||
{
|
||||
mode_t mode = get_mode(fullPath);
|
||||
|
@ -1447,7 +1221,14 @@ void BxlObserver::resolve_path(char *fullpath, bool followFinalSymlink, pid_t as
|
|||
*pFullpath = '\0';
|
||||
// break if the same symlink has already been visited (breaks symlink loops)
|
||||
if (!visited.insert(fullpath).second) break;
|
||||
report_access_internal("_readlink", ES_EVENT_TYPE_NOTIFY_READLINK, fullpath, /* secondPath */ (const char *)nullptr, /* mode */ 0, /* error */ 0, /* checkCache */ true, associatedPid);
|
||||
|
||||
auto event = buildxl::linux::SandboxEvent::AbsolutePathSandboxEvent(
|
||||
/* event_type */ ES_EVENT_TYPE_NOTIFY_READLINK,
|
||||
/* pid */ associatedPid,
|
||||
/* error */ 0,
|
||||
/* src_path */ fullpath);
|
||||
CreateAndReportAccess("_readlink", event);
|
||||
|
||||
*pFullpath = ch;
|
||||
|
||||
// append the rest of the original path to the readlink target
|
||||
|
|
|
@ -246,8 +246,6 @@ private:
|
|||
bool IsCacheHit(es_event_type_t event, const string &path, const string &secondPath);
|
||||
bool CheckCache(es_event_type_t event, const string &path, bool addEntryIfMissing);
|
||||
char** ensure_env_value_with_log(char *const envp[], char const *envName, const char *envValue);
|
||||
void report_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath = nullptr, mode_t mode = 0, int error = 0, bool checkCache = true, pid_t associatedPid = 0);
|
||||
AccessCheckResult create_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode = 0, bool checkCache = true, pid_t associatedPid = 0);
|
||||
ssize_t read_path_for_fd(int fd, char *buf, size_t bufsiz, pid_t associatedPid = 0);
|
||||
|
||||
bool IsMonitoringChildProcesses() const { return !pip_ || CheckMonitorChildProcesses(pip_->GetFamFlags()); }
|
||||
|
@ -293,7 +291,7 @@ private:
|
|||
* If possible, resolves relative or file descriptor paths to absolute paths in a SandboxEvent, and returns true.
|
||||
* This might not be possible if the event is a file-descriptor specified event and the file descriptors
|
||||
* do not correspond to real paths. In this case, the function returns false and the event is unmodified.
|
||||
* In both cases, the mode of the source path of the event is updated
|
||||
* In both cases, the mode of the source path of the event is updated.
|
||||
*/
|
||||
bool ResolveEventPaths(buildxl::linux::SandboxEvent& event);
|
||||
|
||||
|
@ -382,27 +380,6 @@ public:
|
|||
*/
|
||||
void CreateAndReportAccess(const char *syscall_name, buildxl::linux::SandboxEvent& event, bool check_cache = true);
|
||||
|
||||
// The following functions create an access report and performs an access check. They do not report the created access to managed BuildXL.
|
||||
// The created access report is returned as an out param in the given 'report' param. The returned report is ready to be sent with the exception of
|
||||
// setting the operation error. In operations where the error is reported back, the typical flow is creating the report, performing the operation,
|
||||
// setting errno in the report and sending out the report.
|
||||
AccessCheckResult create_access(const char *syscallName, IOEvent &event, AccessReportGroup &report, bool checkCache = true);
|
||||
// In this method (and immediately below) 'mode' is provided on a best effort basis. If 0 is passed for mode, it will be
|
||||
// explicitly computed
|
||||
// NOTE: The associatedPid value for the create_access and report_access functions below take a default value of 0 because they are only set by the ptrace sandbox.
|
||||
// If a value of 0 is set for associatedPid, then it is safe to assume that the report is from the interpose sandbox.
|
||||
AccessCheckResult create_access(const char *syscallName, es_event_type_t eventType, const char *pathname, AccessReportGroup &report, mode_t mode = 0, int oflags = 0, bool checkCache = true, pid_t associatedPid = 0);
|
||||
AccessCheckResult create_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode = 0, bool checkCache = true, pid_t associatedPid = 0);
|
||||
AccessCheckResult create_access_fd(const char *syscallName, es_event_type_t eventType, int fd, AccessReportGroup &reportGroup, pid_t associatedPid = 0);
|
||||
AccessCheckResult create_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, AccessReportGroup &reportGroup, int oflags = 0, bool getModeWithFd = true, pid_t associatedPid = 0);
|
||||
|
||||
// The following functions are the create_* equivalent of the ones above but the access is reported to managed BuildXL
|
||||
void report_access(const char *syscallName, IOEvent &event, bool checkCache = true);
|
||||
void report_access(const char *syscallName, es_event_type_t eventType, const char *pathname, mode_t mode = 0, int oflags = 0, int error = 0, bool checkCache = true, pid_t associatedPid = 0);
|
||||
void report_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, mode_t mode = 0, int error = 0, bool checkCache = true, pid_t associatedPid = 0);
|
||||
void report_access_fd(const char *syscallName, es_event_type_t eventType, int fd, int error, pid_t associatedPid = 0);
|
||||
void report_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, int oflags, bool getModeWithFd = true, pid_t associatedPid = 0, int error = 0);
|
||||
|
||||
// Send a special message to managed code if the policy to override allowed writes based on file existence is set
|
||||
// and the write is allowed by policy
|
||||
void report_firstAllowWriteCheck(const char *fullPath);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче