ffmpeg/libavcodec memory management - memory-leaks

The libavcodec documentation is not very specific about when to free allocated data and how to free it. After reading through documentation and examples, I've put together the sample program below. There are some specific questions inlined in the source but my general question is, am I freeing all memory properly in the code below? I realize the program below doesn't do any cleanup after errors -- the focus is on final cleanup.
The testfile() function is the one in question.
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
}
#include <cstdio>
using namespace std;
void AVFAIL (int code, const char *what) {
char msg[500];
av_strerror(code, msg, sizeof(msg));
fprintf(stderr, "failed: %s\nerror: %s\n", what, msg);
exit(2);
}
#define AVCHECK(f) do { int e = (f); if (e < 0) AVFAIL(e, #f); } while (0)
#define AVCHECKPTR(p,f) do { p = (f); if (!p) AVFAIL(AVERROR_UNKNOWN, #f); } while (0)
void testfile (const char *filename) {
AVFormatContext *format;
unsigned streamIndex;
AVStream *stream = NULL;
AVCodec *codec;
SwsContext *sws;
AVPacket packet;
AVFrame *rawframe;
AVFrame *rgbframe;
unsigned char *rgbdata;
av_register_all();
// load file header
AVCHECK(av_open_input_file(&format, filename, NULL, 0, NULL));
AVCHECK(av_find_stream_info(format));
// find video stream
for (streamIndex = 0; streamIndex < format->nb_streams && !stream; ++ streamIndex)
if (format->streams[streamIndex]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
stream = format->streams[streamIndex];
if (!stream) {
fprintf(stderr, "no video stream\n");
exit(2);
}
// initialize codec
AVCHECKPTR(codec, avcodec_find_decoder(stream->codec->codec_id));
AVCHECK(avcodec_open(stream->codec, codec));
int width = stream->codec->width;
int height = stream->codec->height;
// initialize frame buffers
int rgbbytes = avpicture_get_size(PIX_FMT_RGB24, width, height);
AVCHECKPTR(rawframe, avcodec_alloc_frame());
AVCHECKPTR(rgbframe, avcodec_alloc_frame());
AVCHECKPTR(rgbdata, (unsigned char *)av_mallocz(rgbbytes));
AVCHECK(avpicture_fill((AVPicture *)rgbframe, rgbdata, PIX_FMT_RGB24, width, height));
// initialize sws (for conversion to rgb24)
AVCHECKPTR(sws, sws_getContext(width, height, stream->codec->pix_fmt, width, height, PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL));
// read all frames fromfile
while (av_read_frame(format, &packet) >= 0) {
int frameok = 0;
if (packet.stream_index == (int)streamIndex)
AVCHECK(avcodec_decode_video2(stream->codec, rawframe, &frameok, &packet));
av_free_packet(&packet); // Q: is this necessary or will next av_read_frame take care of it?
if (frameok) {
sws_scale(sws, rawframe->data, rawframe->linesize, 0, height, rgbframe->data, rgbframe->linesize);
// would process rgbframe here
}
// Q: is there anything i need to free here?
}
// CLEANUP: Q: am i missing anything / doing anything unnecessary?
av_free(sws); // Q: is av_free all i need here?
av_free_packet(&packet); // Q: is this necessary (av_read_frame has returned < 0)?
av_free(rgbframe);
av_free(rgbdata);
av_free(rawframe); // Q: i can just do this once at end, instead of in loop above, right?
avcodec_close(stream->codec); // Q: do i need av_free(codec)?
av_close_input_file(format); // Q: do i need av_free(format)?
}
int main (int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s filename\n", argv[0]);
return 1;
}
testfile(argv[1]);
}
Specific questions:
Is there anything I need to free in the frame processing loop; or will libav take care of memory management there for me?
Is av_free the correct way to free an SwsContext?
The frame loop exits when av_read_frame returns < 0. In that case, do I still need to av_free_packet when it's done?
Do I need to call av_free_packet every time through the loop or will av_read_frame free/reuse the old AVPacket automatically?
I can just av_free the AVFrames at the end of the loop instead of reallocating them each time through, correct? It seems to be working fine, but I'd like to confirm that it's working because it's supposed to, rather than by luck.
Do I need to av_free(codec) the AVCodec or do anything else after avcodec_close on the AVCodecContext?
Do I need to av_free(format) the AVFormatContext or do anything else after av_close_input_file?
I also realize that some of these functions are deprecated in current versions of libav. For reasons that are not relevant here, I have to use them.

Those functions are not just deprecated, they've been removed some time ago. So you should really consider upgrading.
Anyway, as for your questions:
1) no, nothing more to free
2) no, use sws_freeContext()
3) no, if av_read_frame() returns an error then the packet does not contain any valid data
4) yes you have to free the packet after you're done with it and before next av_read_frame() call
5) yes, it's perfectly valid
6) no, the codec context itself is allocated by libavformat so av_close_input_file() is
responsible for freeing it. So nothing more for you to do.
7) no, av_close_input_file() frees the format context so there should be nothing more for you to do.

Related

How to handle more than one SIGSEGV occurrence in linux?

I have written a program to scan kernel memory for a pattern from user space. I run it from root. I expect that it will generate SIGSEGVs when it hits pages that aren't accessible; I would like to ignore those faults and just jump to the next page to continue the search. I have set up a signal handler that works fine for the first occurrence, and it continues onward as expected. However, when a second SIGSEGV occurs, the handler is ignored (it was reregistered after the first occurrence) and the program terminates. The relevant portions of the code are:
jmp_buf restore_point;
void segv_handler(int sig, siginfo_t* info, void* ucontext)
{
longjmp(restore_point, SIGSEGV);
}
void setup_segv_handler()
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO|SA_RESTART|SA_RESETHAND;
sigemptyset (&sa.sa_mask);
sa.sa_sigaction = &segv_handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1) {
fprintf(stderr, "failed to setup SIGSEGV handler\n");
}
}
unsigned long search_kernel_memory_area(unsigned long start_address, size_t area_len, const void* pattern, size_t pattern_len)
{
int fd;
char* kernel_mem;
fd = open("/dev/kmem", O_RDONLY);
if (fd < 0)
{
perror("open /dev/kmem failed");
return -1;
}
unsigned long page_size = sysconf(_SC_PAGESIZE);
unsigned long page_aligned_offset = (start_address/page_size)*page_size;
unsigned long area_pages = area_len/page_size + (area_len%page_size ? 1 : 0);
kernel_mem =
mmap(0, area_pages,
PROT_READ, MAP_SHARED,
fd, page_aligned_offset);
if (kernel_mem == MAP_FAILED)
{
perror("mmap failed");
return -1;
}
if (!mlock((const void*)kernel_mem,area_len))
{
perror("mlock failed");
return -1;
}
unsigned long offset_into_page = start_address-page_aligned_offset;
unsigned long start_area_address = (unsigned long)kernel_mem + offset_into_page;
unsigned long end_area_address = start_area_address+area_len-pattern_len+1;
unsigned long addr;
setup_segv_handler();
for (addr = start_area_address; addr < end_area_address;addr++)
{
unsigned char* kmp = (unsigned char*)addr;
unsigned char* pmp = (unsigned char*)pattern;
size_t index = 0;
for (index = 0; index < pattern_len; index++)
{
if (setjmp(restore_point) == 0)
{
unsigned char p = *pmp;
unsigned char k = *kmp;
if (k != p)
{
break;
}
pmp++;
kmp++;
}
else
{
addr += page_size -1;
setup_segv_handler();
break;
}
}
if (index >= pattern_len)
{
return addr;
}
}
munmap(kernel_mem,area_pages);
close(fd);
return 0;
}
I realize I can use functions like memcmp to avoid programming the matching part directly (I did this initially), but I subsequently wanted to insure the finest grained control for recovering from the faults so I could see exactly what was happening.
I scoured the Internet to find information about this behavior, and came up empty. The linux system I am running this under is arm 3.12.30.
If what I am trying to do is not possible under linux, is there some way I can get the current state of the kernel pages from user space (which would allow me to avoid trying to search pages that are inaccessible.) I searched for calls that might provide such information, but also came up empty.
Thanks for your help!
While longjmp is perfectly allowed to be used in the signal handler (the function is known as async-signal-safe, see man signal-safety) and effectively exits from the signal handling, it doesn't restore signal mask. The mask is automatically modified at the time when signal handler is called to block new SIGSEGV signal to interrupt the handler.
While one may restore signal mask manually, it is better (and simpler) to use siglongjmp function instead: aside from the effect of longjmp, it also restores the signal mask. Of course, in that case sigsetjmp function should be used instead of setjmp:
// ... in main() function
if(sigsetjmp(restore_point, 1)) // Aside from other things, store signal mask
// ...
// ... in the signal handler
siglongjmp(restore_point); // Also restore signal mask as it was at sigsetjmp() call

Building a Simple character device but device driver file will not write or read

I am trying to write a simple character device/LKM that reads, writes, and seeks.
I have been having a lot of issues with this, but have been working on it/troubleshooting for weeks and have been unable to get it to work properly. Currently, my module makes properly and mounts and unmounts properly, but if I try to echo to the device driver file the terminal crashes, and when i try to read from it using cat it returns killed.
Steps for this module:
First, I make the module by running make -C /lib/modules/$(uname -r)/build M=$PWD modules
For my kernel, uname -r is 4.10.17newkernel
I mount the module using sudo insmod simple_char_driver.ko
If I run lsmod, the module is listed
If I run dmesg, the KERN_ALERT in my init function "This device is now open" triggers correctly.
Additionally, if I run sudo rmmod, that functions "This device is now closed" KERN_ALERT also triggers correctly.
The module also shows up correctly in cat /proc/devices
I created the device driver file in /dev using sudo mknod -m 777 /dev/simple_char_driver c 240 0
Before making this file, I made sure that the 240 major number was not already in use.
My device driver c file has the following code:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/slab.h>
#include<asm/uaccess.h>
#define BUFFER_SIZE 1024
MODULE_LICENSE("GPL");
//minor nunmber 0;
static int place_in_buffer = 0;
static int end_of_buffer = 1024;
static int MAJOR_NUMBER = 240;
char* DEVICE_NAME = "simple_char_driver";
typedef struct{
char* buf;
}buffer;
char *device_buffer;
static int closeCounter=0;
static int openCounter=0;
ssize_t simple_char_driver_read (struct file *pfile, char __user *buffer, size_t length, loff_t *offset){
int bytesRead = 0;
if (*offset >=BUFFER_SIZE){
bytesRead = 0;
}
if (*offset + length > BUFFER_SIZE){
length = BUFFER_SIZE - *offset;
}
printk(KERN_INFO "Reading from device\n");
if (copy_to_user(buffer, device_buffer + *offset, length) != 0){
return -EFAULT;
}
copy_to_user(buffer, device_buffer + *offset, length);
*offset += length;
printk(KERN_ALERT "Read: %s", buffer);
printk(KERN_ALERT "%d bytes read\n", bytesRead);
return 0;
}
ssize_t simple_char_driver_write (struct file *pfile, const char __user *buffer, size_t length, loff_t *offset){
int nb_bytes_to_copy;
if (BUFFER_SIZE - 1 -*offset <= length)
{
nb_bytes_to_copy= BUFFER_SIZE - 1 -*offset;
printk("BUFFER_SIZE - 1 -*offset <= length");
}
else if (BUFFER_SIZE - 1 - *offset > length)
{
nb_bytes_to_copy = length;
printk("BUFFER_SIZE - 1 -*offset > length");
}
printk(KERN_INFO "Writing to device\n");
if (*offset + length > BUFFER_SIZE)
{
printk("sorry, can't do that. ");
return -1;
}
printk("about to copy from device");
copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy);
device_buffer[*offset + nb_bytes_to_copy] = '\0';
*offset += nb_bytes_to_copy;
return nb_bytes_to_copy;
}
int simple_char_driver_open (struct inode *pinode, struct file *pfile)
{
printk(KERN_ALERT"This device is now open");
openCounter++;
printk(KERN_ALERT "This device has been opened this many times: %d\n", openCounter);
return 0;
}
int simple_char_driver_close (struct inode *pinode, struct file *pfile)
{
printk(KERN_ALERT"This device is now closed");
closeCounter++;
printk(KERN_ALERT "This device has been closed this many times: %d\n", closeCounter);
return 0;
}
loff_t simple_char_driver_seek (struct file *pfile, loff_t offset, int whence)
{
printk(KERN_ALERT"We are now seeking!");
switch(whence){
case 0:{
if(offset<= end_of_buffer && offset >0){
place_in_buffer = offset;
printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer);
}
else{
printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer");
}
break;//THIS IS SEEK_SET
}
case 1:{
if(((place_in_buffer+offset)<= end_of_buffer)&&((place_in_buffer+offset)>0)){
place_in_buffer = place_in_buffer+offset;
printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer);
}
else{
printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer");
}
break;
}
case 2:{//THIS IS SEEK END
if((end_of_buffer-offset)>=0&& offset>0){
place_in_buffer = end_of_buffer-offset;
printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer);
}
else{
printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer");
}
break;
}
default:{
}
}
printk(KERN_ALERT"I sought %d\n", whence);
return place_in_buffer;
}
struct file_operations simple_char_driver_file_operations = {
.owner = THIS_MODULE,
.read = simple_char_driver_read,
.write = simple_char_driver_write,
.open = simple_char_driver_open,
.llseek = &simple_char_driver_seek,
.release = simple_char_driver_close,
};
static int simple_char_driver_init(void)
{
printk(KERN_ALERT "inside %s function\n",__FUNCTION__);
register_chrdev(MAJOR_NUMBER,DEVICE_NAME, &simple_char_driver_file_operations);
device_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
return 0;
}
static void simple_char_driver_exit(void)
{
printk(KERN_ALERT "inside %s function\n",__FUNCTION__);
unregister_chrdev(MAJOR_NUMBER, DEVICE_NAME);
kfree(device_buffer);
}
module_init(simple_char_driver_init);
module_exit(simple_char_driver_exit);
As I said before, this file makes properly with no errors or warnings.
However, currently if I try to echo to the device file
using: echo "hello world" >> /dev/simple_char_driver
The terminal I am using crashes
If I then reopen a terminal, and use: cat /dev/simple_char_driver
then the terminal returns killed.
I am completely lost as to what is going wrong, and I have been searching for a solution for a very long time without success. If anyone has any insight into what is going wrong, please let me know.
Edit: As a user below suggested, I removed all code from my read and write methods except for the printk and the return, to make sure the functions were being triggered.
When I then used echo, dmesg showed that the write printk was triggered, and the device(which I had had open) closed. When I then tried to cat the device file, dmesg showed that the device reopened, the "ready from device" printk showed up succesfully, and then the device closed again. However, echo did not actually find anything to read from the device file, despite my having echoed "Hello world" into it immediately before.
edit
Final functioning read and write functions are as follows:
ssize_t simple_char_driver_read (struct file *pfile, char __user *buffer, size_t length, loff_t *offset)
{
if (*offset > BUFFER_SIZE)
{
printk("offset is greater than buffer size");
return 0;
}
if (*offset + length > BUFFER_SIZE)
{
length = BUFFER_SIZE - *offset;
}
if (copy_to_user(buffer, device_buffer + *offset, length) != 0)
{
return -EFAULT;
}
*offset += length;
return length;
}
ssize_t simple_char_driver_write (struct file *pfile, const char __user *buffer, size_t length, loff_t *offset){
/* *buffer is the userspace buffer where you are writing the data you want to be written in the device file*/
/* length is the length of the userspace buffer*/
/* current position of the opened file*/
/* copy_from_user function: destination is device_buffer and source is the userspace buffer *buffer */
int nb_bytes_to_copy;
if (BUFFER_SIZE - 1 -*offset <= length)
{
nb_bytes_to_copy= BUFFER_SIZE - 1 -*offset;
printk("BUFFER_SIZE - 1 -*offset <= length");
}
else if (BUFFER_SIZE - 1 - *offset > length)
{
nb_bytes_to_copy = length;
printk("BUFFER_SIZE - 1 -*offset > length");
}
printk(KERN_INFO "Writing to device\n");
if (*offset + length > BUFFER_SIZE)
{
printk("sorry, can't do that. ");
return -1;
}
printk("about to copy from device");
copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy);
device_buffer[*offset + nb_bytes_to_copy] = '\0';
*offset += nb_bytes_to_copy;
return nb_bytes_to_copy;
}
Your code in general leaves much to be desired, but what I can see at the moment is that your .write implementation might be dubious. There are two possible mistakes - the absence of buffer boundaries check and disregard of null-termination which may lead to undefined behaviour of strlen().
First of all, you know the size of your buffer - BUFFER_SIZE. Therefore, you should carry out a check that *offset + length < BUFFER_SIZE. It should be < and not <= because anyhow the last byte shall be reserved for null-termination. So, such a check shall make the method return immediately if no space is available (else branch or >=). I can't say for sure whether you should return 0 to report that nothing has been written or use a negative value to return an error code, say, -ENOBUFS or -ENOSPC. Anyhow, the return value of the method is ssize_t meaning that negative value may be returned.
Secondly, if your first check succeeds, your method shall calculate actual space available for writing. I.e., you can make use of MIN(A, B) macro to do this. In other words, you'd better create a variable, say, nb_bytes_to_copy and initialise it like nb_bytes_to_copy = MIN(BUFFER_SIZE - 1 - *offset, length) so that you can use it later in copy_from_user() call. If the user, say, requests to write 5 bytes of data starting at the offset of 1021 bytes, then your driver will allow to write only 2 bytes of the data - say, he instead of hello. Also, the return value shall be set to nb_bytes_to_copy so that the caller will be able to detect the buffer space shortage.
Finally, don't forget about null termination. As soon as you've done with
copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy);
you shall pay attention to do something like
device_buffer[*offset + nb_bytes_copy] = '\0';
Alternatively, if I recall correctly, you may use a special function like strncopy_from_user() to make sure that the data is copied with an implicit null termination.
Also, although a null-terminated write shall not cause problems with subsequent strlen(), I doubt that you ever need it. You can simply do *offset += nb_bytes_to_copy.
By the way, I'd recommend to name the arguments/variables in a more descriptive way. *offset is an eyesore. It would look better if named *offsetp. If your method becomes huge, an average reader will unlikely remember that offset is a pointer and not a value. offsetp where p stands for "pointer" will ease the job of anyone who will support your code in future.
To put it together, I doubt your .write implementation and suggest that you rework it. If some other mistakes persist, you will need to debug them further. Adding debug printouts may come in handy, but please revisit the basic points first, such as null-termination and buffer boundary protection. To make my answer a little bit more useful for you, I furnish it with the link to the section 3.7 of "Linux Device Drivers 3" book which will shed light on the topic under discussion.

sndio sio_onmove not calling back.

I'm trying to write a fullduplex test that copies audio in to audio out. sio_onmove does not get called. I have no idea why. Here's my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <sndio.h>
unsigned char buf[0xffff];
struct sio_hdl *hdl;
void cb(void *arg, int delta) {
int l;
printf("call %d\n", delta);
for(;;) {
l = sio_read(hdl, buf, delta);
if(l==0) break;
sio_write(hdl, buf, l);
}
}
int main(void) {
int m, i;
struct sio_par par;
struct sio_cap cap;
hdl = sio_open("rsnd/0", SIO_PLAY | SIO_REC , 1);
sio_getcap(hdl, &cap);
sio_initpar( &par);
par.bits = cap.enc[0].bits;
par.bps = cap.enc[0].bps;
par.sig = cap.enc[0].sig;
par.le = cap.enc[0].le;
par.msb = cap.enc[0].msb;
par.rchan=cap.rchan[0];
par.pchan=cap.pchan[0];
par.rate =cap.rate[0];
par.appbufsz = 1024;
sio_setpar(hdl, &par);
sio_onmove(hdl, cb, NULL);
sio_start(hdl);
for(;;)
sleep(1);
}
I'm initializing rsnd/0 for recording and play back. The parameters I'm initializing from a getcap call. I'm then setting cb as the callback for onmove. I then start audio. From there I loop forever doing nothing
The sio_onmove() call-back is called either from sio_revents() if non-blocking i/o is used or from blocking sio_read() or sio_write().
As above program calls sleep(1) instead, the call-back is never called.
AFAIU, to do the full-duplex test, you could use blocking i/o (set to 0 last argument of the sio_open() function) and do the following steps:
call sio_initpar() to initialize a sio_par structure, as you do
set your preferred parameters in the sio_par structure
call sio_setpar() to submit them to the device. devices exposed through the server (ex. "snd/0") will accept any parameters, while raw devices (ex. "rsnd/0") pick something close to whatever the hardware supports.
call sio_getpar() to get the parameters the device accepted, this is needed to get the device buffer size
possibly check if they are usable by your program
call sio_start()
prime the play buffer by writing par.bufsz samples with sio_write(). This corresponds to: par.bufsz * par.pchan * par.bps bytes.
At this stage, device starts and you could do the main-loop as with the following pseudo-code:
unsigned char *data;
size_t n, todo, blksz;
blksz = par.round * par.rchan * par.bps;
for (;;) {
/* read one block */
data = buf;
todo = blksz;
while (todo > 0) {
n = sio_read(hdl, data, todo);
if (n == 0)
errx(1, "failed");
todo -= n;
data += n;
}
/* write one block */
n = sio_write(hdl, buf, blksz);
if (n != blksz)
errx(1, "failed");
}
The sio_onmove() call-back is not needed for pure audio programs. It's only useful to synchronize non-audio events (ex video, midi messages) to the audio stream.

Please help me to make this fake character linux device driver work

Hello I am trying to write to a fake char device driver using:
echo > /dev/
and reading it using:
cat /dev/
My problem is that I am getting continuously the first character written printed on the terminal when I do a read with the above mentioned "cat" read method after writing using the echo method above.
My aim is to get the entire set of characters written to the driver back...
I am using dynamic memory allocation for this purpose but not getting the final result after trying many ways of rewriting the code of read() and write() in the driver. Please help..
my Makefile is correct... (I am using ubuntu with a kernel version of 2.6.33...)
My code is as below:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
static dev_t first;
static struct cdev c_dev;
static struct class *cl;
static char* k_buf = NULL;
static int my_open(struct inode *i,struct file *f)
{
printk(KERN_INFO "In driver open()\n");
return 0;
}
static int my_close(struct inode *i,struct file *f)
{
printk(KERN_INFO "In driver close()\n");
return 0;
}
static ssize_t my_read(struct file *f,char __user *buf,size_t len,loff_t *off)
{
printk(KERN_INFO "In driver read()\n");
if(k_buf == NULL)
{
printk(KERN_INFO "You cannot read before writing!\n");
return -1;
}
while(*k_buf != 'EOF')
{
if(copy_to_user(buf,k_buf,1))
return -EFAULT;
off++;
return 1;
}
return 0;
}
static ssize_t my_write(struct file *f,const char __user *buf,size_t len,loff_t *off)
{
printk(KERN_INFO "In driver write()\n");
k_buf = (char*) kmalloc(sizeof(len),GFP_KERNEL);
if(copy_from_user(k_buf,buf,len))
return -EFAULT;
off += len;
return (len);
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_close,
.read = my_read,
.write = my_write
};
static int __init rw_init(void) /*Constructor*/
{
printk(KERN_INFO "hello: rw_ch_driver registered\n");
if(alloc_chrdev_region(&first,0,1,"krishna") < 0)
{
return -1;
}
if ((cl = class_create(THIS_MODULE,"chardev")) == NULL)
{
unregister_chrdev_region(first,1);
return -1;
}
if (device_create(cl,NULL,first,NULL,"rw_char_driver") == NULL)
{
class_destroy(cl);
unregister_chrdev_region(first,1);
return -1;
}
cdev_init(&c_dev,&fops);
if(cdev_add(&c_dev,first,1) == -1)
{
device_destroy(cl,first);
class_destroy(cl);
unregister_chrdev_region(first,1);
return -1;
}
return 0;
}
static void __exit rw_exit(void)/*destructor*/
{
cdev_del(&c_dev);
device_destroy(cl,first);
class_destroy(cl);
unregister_chrdev_region(first,1);
printk(KERN_INFO "bye rw_chardriver unregistered");
}
module_init(rw_init);
module_exit(rw_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("krishna");
MODULE_DESCRIPTION("read write character driver");
Take a careful look at your while loop in my_read().
Most important note first: you don't need this loop. You've put a return statement in it, so it is never going to execute more than once, because the whole function is going to exit when the return is reached. It looks like you're trying to make the function return a single byte at a time repeatedly, but you should just call copy_to_user once, and pass it the number of bytes you want to give back to the user instead. If you only send one character at a time that's fine. It will be up to the user to make the read call again to get the next character.
The nice thing about copy_to_user, is that its return code will tell you if it failed because of bad array bounds, so there's no need to check for EOF on every character. In fact, you are not going to get 'EOF' as a character when you are reading from your buffer because it doesn't exist. Your buffer will store characters and usually a null terminator, '\0', but there is no 'EOF' character in C. EOF is a state you need to identify yourself and report to whoever called open. For the "cat" command, this is done by returning 0 from read. That being said, you should still check your array bounds so we don't end up with another Heartbleed. This SO answer has a good suggestion for how to do bounds checking to make sure you don't send more bytes than your buffer has.
Also, give [this post(https://meta.stackexchange.com/questions/981/syntax-highlighting-language-hints) a read. If you don't have your language in your question tags, it is helpful to other readers to tag your. I've edited your question to clean it up, so you can click "edit" now to see how I did it.

How to determine if code is running in signal-handler context?

I just found out that someone is calling - from a signal handler - a definitely not async-signal-safe function that I wrote.
So, now I'm curious: how to circumvent this situation from happening again? I'd like to be able to easily determine if my code is running in signal handler context (language is C, but wouldn't the solution apply to any language?):
int myfunc( void ) {
if( in_signal_handler_context() ) { return(-1) }
// rest of function goes here
return( 0 );
}
This is under Linux.
Hope this isn't an easy answer, or else I'll feel like an idiot.
Apparently, newer Linux/x86 (probably since some 2.6.x kernel) calls signal handlers from the vdso. You could use this fact to inflict the following horrible hack upon the unsuspecting world:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
uintmax_t vdso_start = 0;
uintmax_t vdso_end = 0; /* actually, next byte */
int check_stack_for_vdso(uint32_t *esp, size_t len)
{
size_t i;
for (i = 0; i < len; i++, esp++)
if (*esp >= vdso_start && *esp < vdso_end)
return 1;
return 0;
}
void handler(int signo)
{
uint32_t *esp;
__asm__ __volatile__ ("mov %%esp, %0" : "=r"(esp));
/* XXX only for demonstration, don't call printf from a signal handler */
printf("handler: check_stack_for_vdso() = %d\n", check_stack_for_vdso(esp, 20));
}
void parse_maps()
{
FILE *maps;
char buf[256];
char path[7];
uintmax_t start, end, offset, inode;
char r, w, x, p;
unsigned major, minor;
maps = fopen("/proc/self/maps", "rt");
if (maps == NULL)
return;
while (!feof(maps) && !ferror(maps)) {
if (fgets(buf, 256, maps) != NULL) {
if (sscanf(buf, "%jx-%jx %c%c%c%c %jx %u:%u %ju %6s",
&start, &end, &r, &w, &x, &p, &offset,
&major, &minor, &inode, path) == 11) {
if (!strcmp(path, "[vdso]")) {
vdso_start = start;
vdso_end = end;
break;
}
}
}
}
fclose(maps);
printf("[vdso] at %jx-%jx\n", vdso_start, vdso_end);
}
int main()
{
struct sigaction sa;
uint32_t *esp;
parse_maps();
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handler;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, NULL) < 0) {
perror("sigaction");
exit(1);
}
__asm__ __volatile__ ("mov %%esp, %0" : "=r"(esp));
printf("before kill: check_stack_for_vdso() = %d\n", check_stack_for_vdso(esp, 20));
kill(getpid(), SIGUSR1);
__asm__ __volatile__ ("mov %%esp, %0" : "=r"(esp));
printf("after kill: check_stack_for_vdso() = %d\n", check_stack_for_vdso(esp, 20));
return 0;
}
SCNR.
If we can assume your application doesn't manually block signals using sigprocmask() or pthread_sigmask(), then this is pretty simple: get your current thread ID (tid). Open /proc/tid/status and get the values for SigBlk and SigCgt. AND those two values. If the result of that AND is non-zero, then that thread is currently running from inside a signal handler. I've tested this myself and it works.
There are two proper ways to deal with this:
Have your co-workers stop doing the wrong thing. Good luck pulling this off with the boss, though...
Make your function re-entrant and async-safe. If necessary, provide a function with a different signature (e.g. using the widely-used *_r naming convention) with the additional arguments that are necessary for state preservation.
As for the non-proper way to do this, on Linux with GNU libc you can use backtrace() and friends to go through the caller list of your function. It's not easy to get right, safe or portable, but it might do for a while:
/*
* *** Warning ***
*
* Black, fragile and unportable magic ahead
*
* Do not use this, lest the daemons of hell be unleashed upon you
*/
int in_signal_handler_context() {
int i, n;
void *bt[1000];
char **bts = NULL;
n = backtrace(bt, 1000);
bts = backtrace_symbols(bt, n);
for (i = 0; i < n; ++i)
printf("%i - %s\n", i, bts[i]);
/* Have a look at the caller chain */
for (i = 0; i < n; ++i) {
/* Far more checks are needed here to avoid misfires */
if (strstr(bts[i], "(__libc_start_main+") != NULL)
return 0;
if (strstr(bts[i], "libc.so.6(+") != NULL)
return 1;
}
return 0;
}
void unsafe() {
if (in_signal_handler_context())
printf("John, you know you are an idiot, right?\n");
}
In my opinion, it might just be better to quit rather than be forced to write code like this.
You could work out something using sigaltstack. Set up an alternative signal stack, get the stack pointer in some async-safe way, if within the alternative stack go on, otherwise abort().
I guess you need to do the following. This is a complex solution, which combines the best practices not only from coding, but from software engineering as well!
Persuade your boss that naming convention on signal handlers is a good thing. Propose, for example, a Hungarian notation, and tell that it was used in Microsoft with great success.
So, all signal handlers will start with sighnd, like sighndInterrupt.
Your function that detects signal handling context would do the following:
Get the backtrace().
Look if any of the functions in it begin with sighnd.... If it does, then congratulations, you're inside a signal handler!
Otherwise, you're not.
Try to avoid working with Jimmy in the same company. "There can be only one", you know.
for code optimized at -O2 or better (istr) have found need to add -fno-omit-frame-pointer
else gcc will optimize out the stack context information

Resources