I have a kernel module that is hooking the read syscall. One of the things I have to do, is capture the contents of the read syscall that is doing an external program submitted by my teacher.
With the strace, I was able to see how the program of my teacher is doing the read:
read(6, "\v\0\0\0\tExercise1", 14)
And the read hooking is working, the problem is, I don't know how to read the contents from inside the new read function because if I am correctly *buf is empty and is not filled until the original syscall read is called. So, in theory I should read directly from the file descriptor, but without using read syscall I don't know how to do that.
Any ideas? Thanks!
Basically, your hooking function should be something like the following :
size_t my_hooked_read(int fildes, void *buf, size_t nbytes)
{
size_t ret;
//Do something before original call
ret = original_read(fildes, buf, nbytes); //call the original read !
//Do something after original call
//buf is correctly filled here !
return ret;
}
If you want to read the content of buf, read it after the original call.
Related
I am using Ubuntu 20.04.3 on a Oracle Virtual Box.
I have few calls to printf() functions that use file descriptors,
but get segmentation fault after the second call.
I tried fsync(fd) and fdatasync(fd) but it didn't solve the problem:
logFileFd = open("./logFile.log", O_APPEND);
if (logFileFd == -1)
{
printf("\ncan't create file %s :[%s]", logFileName, strerror(err));
exit(ERROR_OPEN_LOG_FILE);
}
va_start(a_list, format);
sprintf(logStr, "%s, %s, INFO, %s, %d - ", __DATE__, __TIME__, __BASE_FILE__, __LINE__);
printf("%s", logStr);
dprintf(logFileFd, "%s", logStr);
vprintf(format, a_list);
vdprintf(logFileFd, format, a_list);
va_end(a_list);
close(logFileFd);
the segmentation fault occurs at the line:
vdprintf(logFileFd, format, a_list);
Can any one assist why is that?
Thanks a lot !
Quoting man page for vfprintf(3):
The functions vprintf(), vfprintf(), vsprintf(), vsnprintf() are
equivalent to the functions printf(), fprintf(), sprintf(),
snprintf(), respectively, except that they are called with a va_list
instead of a variable number of arguments. These functions do not call
the va_end macro. Because they invoke the va_arg macro, the value of
ap is undefined after the call.
Additionally on the man page for stdarg(3) you can read that:
If ap is passed to a function that uses va_arg(ap,type) then the value
of ap is undefined after the return of that function.
The problem in your code is that you are using va_list a_list twice - first in call to vprintf(), then in call to vdprintf(). After the first call, a_list value is undefined.
Man stdarg(3) states that "Multiple traversals of the list, each bracketed by va_start() and va_end() are possible". Try applying following modification:
sprintf(logStr, "%s, %s, INFO, %s, %d - ", __DATE__, __TIME__, __BASE_FILE__, __LINE__);
printf("%s", logStr);
dprintf(logFileFd, "%s", logStr);
va_start(a_list, format);
vprintf(format, a_list);
va_end(a_list);
va_start(a_list, format);
vdprintf(logFileFd, format, a_list);
va_end(a_list);
Additionally, please review the flags used in call to open(). According to man page open(2):
The argument flags must include one of the following access
modes: O_RDONLY, O_WRONLY, or O_RDWR
I would propose to apply following modification:
logFileFd = open("./logFile.log", O_WRONLY | O_APPEND);
Thank you (ytta) very very much for your prompted help.
I implemented all your answer and suggestions and it worked perfectly.
The only thing I needed to add (which was not in my question) is the mode_t to the open() function and use the it as:
*int open(const char pathname, int flags, mode_t mode);
setting mode to 0777
because when I added the O_CREAT bit: open("./logFile.log", O_APPEND | OCREAT);
the file logFile.log was created with the sticky bit ON (enabled) in the file system, and in the next iteration the open() failed.
Thanks again very much for your help.
I'm writing a Linux driver in MIPS architecture.
there I implement read operation - read some registers content.
Here is the code:
static int proc_seq_show(struct seq_file *seq, void *v)
{
volatile unsigned reg;
//here read registers and pass to user using seq_printf
//1. read reg number 1
//2. read reg number 2
}
int proc_open(struct inode *inode, struct file *filp)
{
return single_open(filp,&proc_seq_show, NULL);
}
static struct file_operation proc_ops = {.read = &seq_read, .open=&seq_open};
My problem is that reading register content sometimes causing kernel oops - bus error, and read operation is prevented. I can't avoid it in advance.
Since this behavior is acceptable I would like to ignore this error and continue to read the other registers.
I saw bus error handler in the kernel (do_be in traps.c), there is an option to add my own entry to the __dbe_table. An entry looks like that:
struct exception_table_entry {unsigned long insn, nextinsn; };
insn is the instruction that cause the error. nextinsn is the next instruction to be performed after exception.
In the driver I declare an entry:
struct exception_table_entry e __attribute__ ((section("__dbe_table"))) = {0,0};
but I don't know how to initialize it. How can I get the instruction address of the risky line in C? how can I get the address of the fix line? I have tried something with labels and addresses of label - but didn't manage to set correctly the exception_table_entry .
The same infrastructure is available in x86, does someone know how they use it?
Any help will be appreciated.
I know that a call to the glibc "write" function calls in it's turn to the sys_call write function which is a kernel function. because sys_call is a kernel function the CPU has to change the ring to zero store the processes registers and so on.
But does it always switches to kernel mode? for example, if i do
write(-1,buffer,LENGTH)
does it still tries to find it in the file descriptors array?
I see in the glibc source code that it does check for fd>0 but i don't see any jump to the sys_call there (it seems like the baracks for main() ends before any call to the alias_write.
/* Write NBYTES of BUF to FD. Return the number written, or -1. */
ssize_t
__libc_write (int fd, const void *buf, size_t nbytes)
{
if (nbytes == 0)
return 0;
if (fd < 0)
{
__set_errno (EBADF);
return -1;
}
if (buf == NULL)
{
__set_errno (EINVAL);
return -1;
}
__set_errno (ENOSYS);
return -1;
}
libc_hidden_def (__libc_write)
stub_warning (write)
weak_alias (__libc_write, __write)
libc_hidden_weak (__write)
weak_alias (__libc_write, write)
#include <stub-tag.h>
So the question is both:
Where does the glibc actually calls the sys_write
Is it true that glibc doesn't call the sys_write if fd<0?
I see in the glibc source code that it does check for fd>0 but i don't see any jump to the sys_call there
You are looking at the wrong code.
There are multiple definitions of __libc_write used under different conditions. The one you looked at is in io/write.c.
The one that is actually used on Linux is generated from sysdeps/unix/syscall-template.S and it does actually execute the switch to kernel mode (and back to user mode) even when fd==-1, etc.
GetThreadContext is a Windows API.
BOOL WINAPI GetThreadContext(
_In_ HANDLE hThread,
_Inout_ LPCONTEXT lpContext
);
I wonder that how to implement it in linux.
How to retrieves the register information of the specified thread in Linux?
Like this:
pthread_create(thread_id, ...);
...
func(thread_id, reg_info)
{
//get the reg_info by thread_id.
??
}
A Linux-specific way of getting thread info is to use get_thread_area(). From the get_thread_area() man page:
get_thread_area() returns an entry in the current thread's Thread Local Storage (TLS) array. The index of the entry corresponds to the value of u_info->entry_number, passed in by the user. If the value is in bounds, get_thread_area() copies the corresponding TLS entry into the area pointed to by u_info.
But, if you want to read the register value you need to take help of inline assembly. Fox example, to retrieve the value of esp you can use the following inline assembly:
unsigned sp;
__asm __volatile("movl %%esp, %0" : "=r" (sp));
return sp;
In this way, you can extract ebp, eip etc. Hope this will help!
I am trying to send data using the serial port but the write command always returns -1.
This is the code for the write command.
int WriteComm( int Comid, void *buf, int nobtw )
{
unsigned long nobw;
nobw = write(Comid, buf, nobtw);
move(10,5);
perror("");
sleep(10);
return nobw;
}
and this is the code that calls it
gnobw = WriteComm(theApp.idComDev[Seg],&head[1],1); //send network address
I am getting invalid argument as the error but after looking on google I cant find anything explaning what this means or how to fix it. the closes thing I found was this but it uses st0 not ttyS0 so im not sure if its even the same thing.
can anyone explain what i am doing wrong to get this error and how to fix it ?
You should only be examining errno (this includes calling perror()) if the write call failed, which it indicates by returning -1. If the write succeeds, it leaves errno unchanged.
In order to test for this you should really be assigning the return value to a variable with a signed type - preferably ssize_t - not an unsigned long.
You're getting EINVAL back from write( ). That means one of your arguments to the function is invalid: EINVAL = *E*rror, *INVAL*id argument. There are three arguments to the function:
arg your variable
---------------------- -------------
int file descriptor: Comid
void *buf: buf
size_t size: nobtw
write( ) puked when it saw one of those three. So one of those three is wrong.
So put a printf( ) before the call to write( ) and see which one (or two; or three) is wrong.
Where is the actual code (not your memory of the code) that does the open( )? Is the file descriptor returned by open( ) the same one (Comid) you are trying to write( ) onto? If not, there's your problem.
That is the likely error in this mashup.
EINVAL from write(3) means:
The STREAM or multiplexer referenced by fildes is linked (directly or indirectly) downstream from a multiplexer.
What this basically means is that something else has your serial port open for writing at the same time -- at least intermittently. USB to serial converters seem to be particularly vulnerable to this. Other serial drivers will generally only allow you to open them once.
Source:
http://linux.die.net/man/3/write