Opening a file cause system hangs while handling fanotify events - fanotify

I'm newbie to fanotify.
I used the example of the fanotify manpage to write any information to a file, while handling events of file open and close.
A system call to 'fopen' cause system hangs.
When I changed 'FAN_OPEN_PERM' to 'FAN_OPEN', it's all ok,
but 'FAN_OPEN_PERM'flag is not allowed to log file.
Are there something I missed to use fanotify tech. or
any limitations exist for handling fanotify ?
Or any better ideas to log a file while processing fanotify events ?
I've compiled and tested under 'Ubuntu 14.04.3 64bit' with '3.16.0-70-generic' kernel version.
I added some code like this :
static void PrintToFile(const char *pszMsg)
{
int err = 0;
if( NULL == pszMsg) {
printf("invalid message\n");
return ;
}
FILE *fp = fopen("/tmp/fanotify.log", "a+"); // <= here, system hangs
if( NULL == fp ) {
err = errno;
printf("file open fail ( %d ) \n", err);
return ;
}
size_t len = strlen(pszMsg);
feesk(fp, 0L, SEEK_END );
fwrite(pszMsg, 1, len, fp);
fclose(fp);
}
and then, I added the next code to the 'handle_events' fucntion
{
char strBuf[PATH_MAX];
sprintf(strBuf, "File %s\n", path);
PrintToFile(strBuf);
}
See the modifed 'handle_events' function
static void
handle_events(int fd)
{
const struct fanotify_event_metadata *metadata;
struct fanotify_event_metadata buf[200];
ssize_t len;
char path[PATH_MAX];
ssize_t path_len;
char procfd_path[PATH_MAX];
struct fanotify_response response;
/* Loop while events can be read from fanotify file descriptor */
for(;;) {
/* Read some events */
len = read(fd, (void *) &buf, sizeof(buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* Check if end of available data reached */
if (len <= 0)
break;
/* Point to the first event in the buffer */
metadata = buf;
/* Loop over all events in the buffer */
while (FAN_EVENT_OK(metadata, len)) {
/* Check that run-time and compile-time structures match */
if (metadata->vers != FANOTIFY_METADATA_VERSION) {
fprintf(stderr,
"Mismatch of fanotify metadata version.\n");
exit(EXIT_FAILURE);
}
/* metadata->fd contains either FAN_NOFD, indicating a
queue overflow, or a file descriptor (a nonnegative
integer). Here, we simply ignore queue overflow. */
if (metadata->fd >= 0) {
/* Handle open permission event */
if (metadata->mask & FAN_OPEN_PERM) {
printf("FAN_OPEN_PERM: ");
/* Allow file to be opened */
response.fd = metadata->fd;
response.response = FAN_ALLOW;
write(fd, &response,
sizeof(struct fanotify_response));
}
/* Handle closing of writable file event */
if (metadata->mask & FAN_CLOSE_WRITE)
printf("FAN_CLOSE_WRITE: ");
/* Retrieve and print pathname of the accessed file */
snprintf(procfd_path, sizeof(procfd_path),
"/proc/self/fd/%d", metadata->fd);
path_len = readlink(procfd_path, path,
sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
path[path_len] = '\0';
printf("File %s\n", path);
//these code snipptets are added
{
char strBuf[PATH_MAX];
sprintf(strBuf, "File %s\n", path);
PrintToFile(strBuf);
}
/* Close the file descriptor of the event */
close(metadata->fd);
}
/* Advance to next event */
metadata = FAN_EVENT_NEXT(metadata, len);
}
}
}

Not sure but opening a file in a "file open" monitor handler may cause infinite loop. Try adding:
if (metadata->pid != getpid())
before the block with the call to PrintToFile to ignore printing events cause but your program itself.

FAN_OPEN_PERM flag request a response from you whether allow to open file nor deny and it blocks events until you answer to kernel that what it should do with that file

Related

how to alloc user space memory in kernel space?

I hook a syscall(open) on Linux, and want to print this opened filename.
then I call syscall(getcwd) to get the absolute path.
this is source code:
void *memndup_from_user(const void __user *src, long len)
{
void *kbuf = NULL;
if(src == NULL) {
return kbuf;
}
kbuf = kmalloc(len + 1, GFP_KERNEL);
if(kbuf != NULL) {
if (copy_from_user(kbuf, src, len)) {
printk(KERN_ALERT "%s\n", "copy_from_user failed.");
kfree(kbuf);
kbuf = NULL;
}
else {
((char *)kbuf)[len] = '\0';
}
} else {
printk(KERN_ALERT "%s\n", "kmalloc failed.");
}
return kbuf;
}
void *memdup_from_user(const void __user *src)
{
long len = 0;
if(src == NULL) {
return NULL;
}
len = strlen_user(src);
return memndup_from_user(src, len);
}
asmlinkage long fake_getcwd(char __user *buf, unsigned long size)
{
return real_getcwd(buf, size);
}
asmlinkage long
fake_open(const char __user *filename, int flags, umode_t mode)
{
if(flags & O_CREAT) {
char *k_filename = (char *)memdup_from_user(filename);
char *u_path = (char *)kmalloc(PAGE_SIZE, GFP_USER);
if(k_filename != NULL) {
printk(KERN_ALERT "ano_fake_open pid:%ld create : %s\n", ano_fake_getpid(), k_filename);
kfree(k_filename);
}
if(u_path != NULL) {
long retv;
retv = fake_getcwd(u_path, PAGE_SIZE);
if(retv > 0) {
printk(KERN_ALERT "getcwd ret val: %ld, path: %s\n", retv, u_path);
} else {
printk(KERN_ALERT "getcwd ret val: %ld, error...\n", retv);
}
kfree(u_path);
}
}
return real_open(filename, flags, mode);
}
the sys_getcwd requires an user space memory, and I call kmalloc with GFP_USER.
but sys_getcwd always return -EFAULT(Bad Address)...
this is dmesg logs:
[344897.726061] fake_open pid:70393 create : sssssssssssssssss
[344897.726065] getcwd ret val: -14, error...
[344897.727431] fake_open pid:695 create : /var/lib/rsyslog/imjournal.state.tmp
[344897.727440] getcwd ret val: -14, error...
so I find the implement in sys_getcwd, he does
# define __user __attribute__((noderef, address_space(1)))
# define __kernel __attribute__((address_space(0)))
#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL)
SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
{
char *page = __getname();
get_fs_root_and_pwd_rcu(current->fs, &root, &pwd);
...
// char *cwd = page + xxx; (xxx < PAGE_SIZE)
// len = PAGE_SIZE + page - cwd;
...
if (len <= size) {
error = len;
if (copy_to_user(buf, cwd, len))
error = -EFAULT;
}
}
obviously, getcwd alloc memory with flag GFP_KERNEL, then copy to my buffer( __user *buf ) from (GFP_KERNEL) !!!
isn't __user MACRO be GFP_USER ?
the flag GFP_USER brief is https://elixir.bootlin.com/linux/v4.4/source/include/linux/gfp.h#L208:
/* GFP_USER is for userspace allocations that also need to be directly
* accessibly by the kernel or hardware. It is typically used by hardware
* for buffers that are mapped to userspace (e.g. graphics) that hardware
* still must DMA to. cpuset limits are enforced for these allocations.
*/
what's wrong ?
This is wrong on at least two accounts:
syscall hijacking (let alone for something like open) is just a bad idea. the only sensible method to catch all possible open path is through using LSM hooks. it also happens to deal with the actual file being opened avoiding the race: you read the path in your routine, wrapped opens reads it again. but by that time malicious userspace could have changed it and you ended up looking at the wrong file.
it should be clear getcwd has to have a method of resolving a name in order to put it into the userspace buffer. you should dig in into the call and see what can be changed to put it in a kernel buffer.
Why are you doing this to begin with?

Parsing events in real time ETW consumer on Windows

We are working on ETW real time consumer application by referring to https://msdn.microsoft.com/en-us/library/windows/desktop/aa364157(v=vs.85).aspx sample.
We have been successful getting callback and print "ParentGuid" of EVENT_TRACE structure within callback. However we are getting MofData pointer as always NULL and MofLength as always 0 (zero).
On the other hand if we use non real time ETW consumer method i.e. file mode; reading from .etl file we are able to get valid MofData pointer.
We are trying to consume Kernel events such as CPU usage, DISK IO details from Events in real time.
So does it mean we cannot consume Kernel events in real time? Can some one suggest why we are not getting valid pointer/MofData?
// ConsoleApplication5.cpp : Defines the entry point for the console application.
//
//Turns the DEFINE_GUID for EventTraceGuid into a const.
#define INITGUID
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <evntrace.h>
#define LOGSESSION_NAME L"power"
// Used to calculate CPU usage
ULONG g_TimerResolution = 0;
void WINAPI ProcessEvent(PEVENT_TRACE pEvent);
void wmain(void)
{
ULONG status = ERROR_SUCCESS;
EVENT_TRACE_LOGFILE trace;
TRACE_LOGFILE_HEADER* pHeader = &trace.LogfileHeader;
TRACEHANDLE hTrace = 0;
HRESULT hr = S_OK;
// Identify the log file from which you want to consume events
// and the callbacks used to process the events and buffers.
ZeroMemory(&trace, sizeof(EVENT_TRACE_LOGFILE));
trace.LoggerName = (LPWSTR)LOGSESSION_NAME;
trace.CurrentTime = 0;
trace.BuffersRead = 0;
trace.BufferSize = 0;
trace.Filled = 0;
trace.EventsLost = 0;
trace.Context = NULL;
trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
trace.EventCallback = (PEVENT_CALLBACK)(ProcessEvent);
trace.BufferCallback = (PEVENT_TRACE_BUFFER_CALLBACK)(ProcessBuffer);
hTrace = OpenTrace(&trace);
if ((TRACEHANDLE)INVALID_HANDLE_VALUE == hTrace)
{
wprintf(L"OpenTrace failed with %lu\n", GetLastError());
goto cleanup;
}
if (pHeader->TimerResolution > 0)
{
g_TimerResolution = pHeader->TimerResolution / 10000;
}
wprintf(L"Number of events lost: %lu\n", pHeader->EventsLost);
// Use pHeader to access all fields prior to LoggerName.
// Adjust pHeader based on the pointer size to access
// all fields after LogFileName. This is required only if
// you are consuming events on an architecture that is
// different from architecture used to write the events.
if (pHeader->PointerSize != sizeof(PVOID))
{
pHeader = (PTRACE_LOGFILE_HEADER)((PUCHAR)pHeader +
2 * (pHeader->PointerSize - sizeof(PVOID)));
}
wprintf(L"Number of buffers lost: %lu\n\n", pHeader->BuffersLost);
status = ProcessTrace(&hTrace, 1, 0, 0);
if (status != ERROR_SUCCESS && status != ERROR_CANCELLED)
{
wprintf(L"ProcessTrace failed with %lu\n", status);
goto cleanup;
}
cleanup:
if ((TRACEHANDLE)INVALID_HANDLE_VALUE != hTrace)
{
status = CloseTrace(hTrace);
}
}
VOID WINAPI ProcessEvent(PEVENT_TRACE pEvent)
{
PBYTE pEventData = NULL;
pEventData = (PBYTE)(pEvent->MofData);
printf("\n hi%d", pEventData);
printf("\n length %d", pEvent->MofLength);
}

interacting user space during system call

I'm trying to write block device driver that implements read/write operations.
The tricky thing is that the information is not in the hardware, but in a user space process. Therefore, during the read/write system call I would like to interact the user space (i.e. sendign signal to the user space).
However, my user space process catching the signal only after the read/write system call returned. adding wait in the system call implementation seems to be ignored somehow.
I used this code at the read system call:
ssize_t sleepy_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
struct siginfo info;
struct task_struct *t;
int ret;
#define SIG_TEST 44
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIG_TEST;
info.si_code = SI_QUEUE;
info.si_int = 1234;
rcu_read_lock();
t = pid_task(find_pid_ns(current->pid, &init_pid_ns), PIDTYPE_PID);
if(t == NULL){
printk(KERN_ERR "no such pid\n");
rcu_read_unlock();
return -ENODEV;
}
rcu_read_unlock();
ret = send_sig_info(SIG_TEST, &info, t); //send the signal
if (ret < 0) {
printk("error sending signal\n");
return ret;
}
wait_event_interruptible(wq, flag != 0);
msleep(10000);
return (0);
}
and this code at user space:
#define SIG_TEST 44
int g_devFile = -1;
void receiveData(int n, siginfo_t *info, void *unused)
{
printf("received value %i\n", info->si_int);
}
int main(void)
{
struct sigaction sig;
sig.sa_sigaction = receiveData;
sig.sa_flags = SA_SIGINFO;
sigaction(SIG_TEST, &sig, NULL);
g_devFile = open(devname, O_RDWR);
if ( g_devFile < 0 ) {
fprintf(stderr,"Error opening device[%s] file err[%s]\n",devname,strerror(errno));
return -1;
} else {
fprintf (stderr, "device opened. ptr=%p\n", (void*)g_devFile);
}
i = read(g_devFile, &buff, 11);
}
Currently I'm catching my signal (in user space) only after the 10 seconds sleep expieres (the wait seems to be ignored).
Any idea will be appriceated. Thanks.

DuplicateHandle for a file

I'm trying to make DuplicateHandle() for a file that another process writes. I succeeded, but I get the position of the owner process. After I seek to the beginning it seeks also in the owner process. Can I somehow seek without changing the first process's progress?
EDIT:
Another application opens this file without CreateFile. Is thare a way to read the file form the begining with ReadFile, without seeking manually?
EDIT again:
There isn't a way to read only from one side with duplicated handle. Thanks for helping.
From MSDN:
The duplicate handle refers to the same object as the original handle. Therefore, any changes to the object are reflected through both handles. For example, if you duplicate a file handle, the current file position is always the same for both handles. For file handles to have different file positions, use the CreateFile function to create file handles that share access to the same file.
Instead of DuplicateHandle, you must call CreateFile in both process, with the right combination of access mode and sharing flag. MSDN has the full set of rules, here is a combination that works :
Writer process :
HANDLE file = CreateFile(..., GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, ...);
Reader process :
HANDLE file = CreateFile(..., GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, ...);
If you need to play with the flags, here is the (crude) test application I wrote to answer your question :
// 2process1file.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#define NUMBER_OF_LINES 100
#define IO_PERIOD 250
static const char message[] = "The quick brown fox jumps over the lazy dog.\n";
HANDLE file = INVALID_HANDLE_VALUE;
BOOL CtrlHandler(DWORD ctltype)
{
if(file != INVALID_HANDLE_VALUE)
{
CloseHandle(file);
file = INVALID_HANDLE_VALUE;
}
return FALSE;
}
int _tmain(int argc, _TCHAR* argv[])
{
if(argc == 3)
{
DWORD access = GENERIC_READ;
DWORD share = FILE_SHARE_READ;
bool is_writer = false;
if((*argv[1]|' ') == 'w')
{
access |= GENERIC_WRITE;
is_writer = true;
}
else
{
share |= FILE_SHARE_WRITE;
}
file = CreateFile(argv[2], access, share, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if(file != INVALID_HANDLE_VALUE)
{
DWORD nbytes = 1;
SetFilePointer(file, 0, 0, FILE_BEGIN); //Redundant when writing
for(int i=0; (i<NUMBER_OF_LINES) && nbytes; ++i) {
if(is_writer) {
if(WriteFile(file, message, sizeof(message)-1, &nbytes, 0) == 0)
{
//Write failed somehow
break;
}
//Sleep(INFINITE);
if(i%25 == 0) printf("%d\n", i);
} else {
char buffer[sizeof message] = "";
if(ReadFile(file, buffer, sizeof(buffer)-1, &nbytes, 0) && nbytes) {
buffer[sizeof(buffer)-1] = 0;
printf(buffer);
} else {
//Read failed somehow
break;
}
}
Sleep(IO_PERIOD);
}
CloseHandle(file);
file = INVALID_HANDLE_VALUE;
}
}
else
{
wprintf(L"Usage : %s [w|r] filename\n");
}
return 0;
}

renaming multiple file

int rename_file()
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
hFind = FindFirstFile(L"\\Hard Disk\\*.*", &FindFileData);
LPTSTR oldfilename;
LPTSTR newfilename;
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("FindFirstFile failed (%d)\n", GetLastError());
return 0;
}
else
{
int i=1000;
while (FindNextFile(hFind, &FindFileData) != 0)
{
_tprintf (TEXT("The first file found is %s\n"),FindFileData.cFileName);
oldfilename =FindFileData.cFileName;
StringCchPrintf(newfilename, 30, TEXT("%s\\newfile_%d.txt"),dirname, i);
BOOL rs = MoveFile(oldfilename,newfilename);
i++;
}
FindClose(hFind);
return 1;
}
}
i am unable to rename file ,i am working on wince 6 ,while debugging at StringCchPrintf iam getting exception in coredll.dll can any one help me ....
You have not allocated any buffer for newFileName, so when you use it in the StringCchPrintf it's just an uninitialized pointer.
Try this:
TCHAR newFile[260]; // or whatever length you wish
LPTSTR newfilename = &newFile[0];
Also you should check the return code from MoveFile, and output something sensible on error. Make a habit of doing this for all your function calls that can return an error.

Resources