Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/execve-linux-null-envp
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/atomic-write
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/write-EBADF
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/change-write-result
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/flock
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/inherited-flocks
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/mmap-syscall
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/access-itself
EXAMPLE_PROGRAMS += $(EXAMPLE_DST)/sockets
Expand Down Expand Up @@ -74,3 +76,7 @@ $(EXAMPLE_DST)/%: $(EXAMPLE_SRC)/%.c
example-programs-build/mmap-syscall: example-programs/mmap-syscall.c
mkdir -p example-programs-build
gcc -static -std=c99 -Wall -Werror example-programs/mmap-syscall.c -o example-programs-build/mmap-syscall

example-programs-build/inherited-flocks: example-programs/inherited-flocks.c
mkdir -p example-programs-build
gcc -static -std=c99 -Wall -Werror -pthread example-programs/inherited-flocks.c -o example-programs-build/inherited-flocks
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Includes:
## Use cases

* **General**
* Get all syscalls in a list and process them programatically.
* Get all syscalls in a list and process them programmatically.
* Audit high-assurance software systems.
* Debug difficult bugs that occur only in certain rare situations.
* Change the results of system calls as seen by the traced program.
Expand All @@ -19,7 +19,7 @@ Includes:
* Kill your build tool at the 3rd `write()` syscall to an `.o` file, checking whether it will recover from that in the next run.
* **Testing**
* Write test suites that assert how your code uses system calls, for correctness or performance.
* Mock syscalls to test how your program would behave in situations that are difficult to create in the real world, for example [veryfing fault-tolerance in specific scenarios](https://tgrez.github.io/posts/2019-09-04-simulate-network-failures.html)
* Mock syscalls to test how your program would behave in situations that are difficult to create in the real world, for example [verifying fault-tolerance in specific scenarios](https://tgrez.github.io/posts/2019-09-04-simulate-network-failures.html)
* Implement anomaly test suites [like `sqlite` does](https://www.sqlite.org/testing.html#i_o_error_testing), exhaustively testing whether your program can recover from a crash in _any_ syscall.
* **Fuzzing**
* Insert garbage data into the program by changing syscall results or directly changing its memory contents.
Expand All @@ -28,6 +28,17 @@ Includes:
* Add "magic" support for new file systems without modifying existing programs (like [this paper](https://www.usenix.org/legacy/events/expcs07/papers/22-spillane.pdf) shows).
* Add logging capabilities to programs that were designed without.

## Building / Installing

* Clone: `git clone --recursive https://github.com/nh2/hatrace && cd hatrace`
* After fetching new changes, update submodules with: `git submodule init && git submodule update --init --recursive`
* Building for development:
* With [Stack](https://docs.haskellstack.org/en/stable/README/): `stack build` or even better `stack test`
* With [Cabal](https://www.haskell.org/cabal/): `cabal build` or even better `cabal test`
* Installing:
* With Stack: `stack install`
* With Cabal: `cabal install`

## Work in progress

This software is work in progress.
Expand All @@ -37,12 +48,12 @@ The `hatrace` executable is extremely basic and can't do much.
While syscall names are automatically generated, detail data needs to be implemented by hand and is done for only a few so far.
Help to add more is appreciated.

However, the Haskell API to write scripts can already do a log. Take a look at the test suite for examples.
However, the Haskell API to write scripts can already do a lot. Take a look at the test suite for examples.

### TODO list for contributors

If you find any of the below topics interesting give it a shot!
It is recommended to file an issue when picking up one of the tasks to coordinate against doing duplicate work.
It is recommended to file an issue when picking up one of the tasks to coordinate and avoid doing duplicate work.

* [ ] Implement all the syscalls
* [X] Remembering syscall arguments in a PID/TID map
Expand Down
2 changes: 1 addition & 1 deletion cabal.project
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
packages: .

# Keep deps in sync with `stack.yaml`.
-- Keep deps in sync with `stack.yaml`.

source-repository-package
type: git
Expand Down
26 changes: 26 additions & 0 deletions example-programs/flock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <stdio.h>
#include <sys/file.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
if (argc != 2) {
puts("flock /path/to/file/to/lock\n");
return 1;
}

const char* filepath = argv[1];

int fd = open(filepath, O_RDWR);

if (fd < 0) {
perror("open");
return 1;
}

if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
perror("flock");
return 2;
}

return 0;
}
72 changes: 72 additions & 0 deletions example-programs/inherited-flocks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <errno.h>
#include <stdio.h>
#include <sys/file.h>
#include <unistd.h>
#include <pthread.h>

void *myThreadFun(void *vargp) {
// Store the value argument passed to this thread
int fd = *((int *)vargp);

printf("Thread started, got passed fd %d\n", fd);

sleep(2);

puts("Thread exiting\n");

return 0;
}

int main(int argc, char* argv[]) {
if (argc != 2) {
puts("flock /path/to/file/to/lock\n");
return 1;
}

const char* filepath = argv[1];

int fd = open(filepath, O_RDWR);

if (fd < 0) {
perror("open");
return 1;
}

if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
perror("flock");
return 2;
}

pthread_t tid;
int ret = pthread_create(&tid, NULL, myThreadFun, (void *)&fd);

if (ret != 0) {
errno = ret; // pthread_create() does not set errno, but returns it
perror("pthread_create");
return 3;
}

printf("Main started side thread; closing FD %d to unlock, and sleeping for 1 second\n", fd);

close(fd);

sleep(1);

puts("Main re-opening and re-locking file");

fd = open(filepath, O_RDWR);

if (fd < 0) {
perror("open");
return 1;
}

if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
perror("flock");
return 2;
}

pthread_exit(NULL); // exit main thread and wait for all threads to finish

return 0;
}
1 change: 1 addition & 0 deletions hatrace.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ library
System.Hatrace.SyscallTables
System.Hatrace.SyscallTables.Generated
System.Hatrace.SyscallTables.Util
System.Hatrace.Tools
System.Hatrace.Types
System.Hatrace.Types.Internal
System.Hatrace.Types.TH
Expand Down
55 changes: 55 additions & 0 deletions src/System/Hatrace.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ module System.Hatrace
, SyscallExitDetails_access(..)
, SyscallEnterDetails_faccessat(..)
, SyscallExitDetails_faccessat(..)
, SyscallEnterDetails_flock(..)
, SyscallExitDetails_flock(..)
, SyscallEnterDetails_write(..)
, SyscallExitDetails_write(..)
, SyscallEnterDetails_read(..)
Expand Down Expand Up @@ -140,9 +142,14 @@ module System.Hatrace
, SyscallExitDetails_rmdir(..)
, DetailedSyscallEnter(..)
, DetailedSyscallExit(..)
, EnterDetails(..)
, enterDetailsToSyscall
, ExitDetails(..)
, getExitedSyscallResult
, ERRNO(..)
, foreignErrnoToERRNO
, getSyscallEnterDetails
, getSyscallExitDetails
, setExitedSyscallResult
, syscallEnterDetailsOnlyConduit
, syscallRawEnterDetailsOnlyConduit
Expand All @@ -161,8 +168,15 @@ module System.Hatrace
, doesProcessHaveChildren
, getFdPath
, getExePath
-- * Simplified API that looks arguments only at syscall enter time
, HatraceEvent(..)
, EventDetails(..)
, EventSyscallEnterDetails(..)
, EventSyscallExitDetails(..)
, ReturnOrErrno(..)
-- * Re-exports
, KnownSyscall(..)
, CPid
) where

import Conduit (concatMapC, foldlC)
Expand Down Expand Up @@ -846,6 +860,29 @@ instance SyscallExitFormatting SyscallExitDetails_faccessat where
(syscallEnterToFormatted enterDetail, NoReturn)


data SyscallEnterDetails_flock = SyscallEnterDetails_flock
{ fd :: CInt
, operation :: CInt
-- Peeked details
, flockOperation :: FlockOperation
} deriving (Eq, Ord, Show)

instance SyscallEnterFormatting SyscallEnterDetails_flock where
syscallEnterToFormatted SyscallEnterDetails_flock{ fd, operation, flockOperation } =
FormattedSyscall "flock" [ formatArg fd, formatArg operation
, formatArg flockOperation
]


data SyscallExitDetails_flock = SyscallExitDetails_flock
{ enterDetail :: SyscallEnterDetails_flock
} deriving (Eq, Ord, Show)

instance SyscallExitFormatting SyscallExitDetails_flock where
syscallExitToFormatted SyscallExitDetails_flock{ enterDetail } =
(syscallEnterToFormatted enterDetail, NoReturn)


data SyscallEnterDetails_stat = SyscallEnterDetails_stat
{ pathname :: Ptr CChar
, statbuf :: Ptr StatStruct
Expand Down Expand Up @@ -1844,6 +1881,7 @@ data DetailedSyscallEnter
| DetailedSyscallEnter_pipe2 SyscallEnterDetails_pipe2
| DetailedSyscallEnter_access SyscallEnterDetails_access
| DetailedSyscallEnter_faccessat SyscallEnterDetails_faccessat
| DetailedSyscallEnter_flock SyscallEnterDetails_flock
| DetailedSyscallEnter_write SyscallEnterDetails_write
| DetailedSyscallEnter_read SyscallEnterDetails_read
| DetailedSyscallEnter_execve SyscallEnterDetails_execve
Expand Down Expand Up @@ -1907,6 +1945,7 @@ data DetailedSyscallExit
| DetailedSyscallExit_pipe2 SyscallExitDetails_pipe2
| DetailedSyscallExit_access SyscallExitDetails_access
| DetailedSyscallExit_faccessat SyscallExitDetails_faccessat
| DetailedSyscallExit_flock SyscallExitDetails_flock
| DetailedSyscallExit_write SyscallExitDetails_write
| DetailedSyscallExit_read SyscallExitDetails_read
| DetailedSyscallExit_execve SyscallExitDetails_execve
Expand Down Expand Up @@ -2057,6 +2096,13 @@ getSyscallEnterDetails syscall syscallArgs pid = let proc = TracedProcess pid in
, pathnameBS
, flags = fromIntegral flags
}
Syscall_flock -> do
let SyscallArgs{ arg0 = fd, arg1 = operation } = syscallArgs
pure $ DetailedSyscallEnter_flock $ SyscallEnterDetails_flock
{ fd = fromIntegral fd
, operation = fromIntegral operation
, flockOperation = fromCInt (fromIntegral operation)
}
Syscall_write -> do
let SyscallArgs{ arg0 = fd, arg1 = bufAddr, arg2 = count } = syscallArgs
let bufPtr = word64ToPtr bufAddr
Expand Down Expand Up @@ -2619,6 +2665,11 @@ getSyscallExitDetails detailedSyscallEnter result pid =
pure $ DetailedSyscallExit_faccessat $
SyscallExitDetails_faccessat{ enterDetail }

DetailedSyscallEnter_flock
enterDetail@SyscallEnterDetails_flock{} -> do
pure $ DetailedSyscallExit_flock $
SyscallExitDetails_flock{ enterDetail }

DetailedSyscallEnter_read
enterDetail@SyscallEnterDetails_read{ buf } -> do
bufContents <- peekBytes (TracedProcess pid) buf (fromIntegral result)
Expand Down Expand Up @@ -3312,6 +3363,8 @@ formatSyscallEnter enterDetails =

DetailedSyscallEnter_faccessat details -> syscallEnterToFormatted details

DetailedSyscallEnter_flock details -> syscallEnterToFormatted details

DetailedSyscallEnter_write details -> syscallEnterToFormatted details

DetailedSyscallEnter_read details -> syscallEnterToFormatted details
Expand Down Expand Up @@ -3467,6 +3520,8 @@ formatDetailedSyscallExit detailedExit handleUnimplemented =

DetailedSyscallExit_faccessat details -> formatDetails details

DetailedSyscallExit_flock details -> formatDetails details

DetailedSyscallExit_write details -> formatDetails details

DetailedSyscallExit_read details -> formatDetails details
Expand Down
16 changes: 13 additions & 3 deletions src/System/Hatrace/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import System.FilePath (splitPath)

import System.Hatrace
import System.Hatrace.Format
import System.Hatrace.Tools

data Filter =
FilterAtomicWrites
data Filter
= FilterAtomicWrites
| FilterIntheritedFlocks
deriving (Eq, Ord, Show)

-- | Command line arguments of this program.
Expand Down Expand Up @@ -50,11 +52,15 @@ cliArgsParser = do

modeParser :: Parser RunMode
modeParser =
filterParser Opts.<|> traceParser
filterParser Opts.<|> filterInheritedFlocksParser Opts.<|> traceParser
where
filterParser = flag' (FilterMode FilterAtomicWrites)
( long "find-nonatomic-writes"
<> help "find file writes without a following rename to a persistent location" )
filterInheritedFlocksParser =
flag' (FilterMode FilterIntheritedFlocks)
( long "find-inherited-flocks"
<> help "find flock()s on locks that were inherided forked threads/processes" )
traceParser = TraceMode <$> (traceJsonParser Opts.<|> traceStdParser)
traceJsonParser = flag' JsonOutput
( long "json-output"
Expand Down Expand Up @@ -122,6 +128,10 @@ main = do
forM_ bad $ \(p, e) -> do
putStrLn $ " - " ++ show p ++ ": " ++ e
exitWith exitCode
FilterMode FilterIntheritedFlocks -> do
argv <- procToArgv cliProgram cliArgs
res <- sourceTraceForkExecvFullPathWithSink argv inheritedFlocksSink
print res

maybeNonatomicOrBad :: FilePath -> FileWriteBehavior -> Maybe (Either FilePath (FilePath, String))
maybeNonatomicOrBad _ NoWrites = Nothing
Expand Down
Loading