Linux kernel struct file pointer - linux

Is it guaranteed that a struct file pointer won't be deallocated and reallocated somewhere else in memory during its open to close lifecycle?
I want to uniquely identify file structs that are passed to a device driver (through read/write/open etc) and was wondering if I could just use the pointer to the file struct for identification. The only other alternative I see would be to store a unique identifier in private_data, if it is not guaranteed that the struct file pointer will not change.

Nothing will happen to the pointer. But you have to make sure that if this pointer is being passed across the kernel-user boundary (or computer-network), you actually check that the pointer you get is one of the valid pointers and perhaps an appropriate one (expected from this particular caller, if you can identify them). Otherwise you will have a huge security hole.

Related

Implementing LSM hook bprm_check_security

Recently, I am working on developing an Application Whitelisting solution for embedded linux based on the Linux Security Framework. The main focus of my LSM is implementing the bprm_check_security hook, invoked, when a program executing in the user-space (we do not consider kernel processes).
This hook is given a pointer of type "struct linux_binprm *bprm". This pointer includes a file pointer (including the executable file of the executed program), and a char pointer (including the name of the executed program).
Our application whitelisting solution is based on hash calculation. Accordingly, in my LSM, I use the file pointer(contained in the bprm pointer) to calculate a new hash value and store that value together with the filename (in the bprm pointer) as an entry in a list.
However, during the linux boot (before the /sbin/init is executed), there are missmatches between the filename, and the file pointer.
For instance, in one of first executing programs, the filename in the bprm pointer is "/bin/cat", however, the file pointer in the same bprm pointer is not the actual file of /bin/cat, rather busybox.
After researching for a long time, I found out, that those files are executed by busybox to create an initial initrd, which consequently create the actual rootfs, and all of those files have the magic number RAMFS_MAGIC (stored in inode->i_sb->s_magic). So I used this number to filter those processes, however, I am not sure, whether it would be the right way or not. I would appreciate any helps.
It is to be noted that, I use the file pointer (included in the bprm pointer) to calculate the hash values, in other words, I dont read files depending on their filename or filepath from the userspace.
thanks.
/include/linux/binfmts.h
struct linux_binprm {
struct file * file;
const char * filename; /* Name of binary as seen by procps */
};

How linux identify a particular file system to execute system call

Can please summarize the events/steps that happen when I try to execute a read()/write() system call. How does the kernel know which file system to issue these commands.
Lets say a process calls write().
Then It will call sys_write().
Now probably, since sys_write() is executed on behalf of the current process, it can access the struct task_struct and hence it can access the struct files_struct and struct fs_struct which contains file system information.
But after that I am not seeing, how this fs_struct is helping to identify the file system.
Edit: Now that Alex has described the flow...I have still doubt how the read/write are getting routed to a FS, since the VFS does not do it, then it must be happening somewhere else, Also how is the underlying block device and then finally the hardware protocol PCI/USB getting attached.
A simple flow chart involving actual data structures would be helpful
Please help.
This answer is based on kernel version 4.0. I traced out some of the code which handles a read syscall. I recommend you clone the Linux source repo and follow along in the source code.
Syscall handler for read, at fs/read_write.c:620 is called. It receives a file descriptor (integer) as an argument, and calls fdget_pos to convert it to a struct fd.
fdget_pos calls __fdget_pos calls __fdget calls __fget_light. __fget_light uses current->files, the file descriptor table for the current process, to look up the struct file which corresponds to the passed file descriptor number.
Back in the syscall handler, the file struct is passed to vfs_read, at fs/read_write.c:478.
vfs_read calls __vfs_read, which calls file->f_op->read. From here on, you are in filesystem-specific code.
So the VFS doesn't really bother "identifying" the filesystem which a file lives on; it simply uses the table of "file operation" function pointers which is stored in its struct file. When that struct file is initialized, it is given the correct f_op function pointer table which implements all the filesystem-specific operations for its filesystem.
Each filesystem registers itself to VFS. When a filesystem is mounted, its superblock is read and VFS superblock is populated with this information. Function pointer table for this filesystem is also populated at this time. when file->f_op->read call happens, registered function from the filesystem is actually called. You can refer to text in http://www.science.unitn.it/~fiorella/guidelinux/tlk/node102.html

Are 32 bit pointers valid in 64 bit process?

Here is my issue. I implemented a Win 7 x64 process talking to a x32 process by following this link
The x64 process retrieves fine a x32 pointer (p_x32 below) to myClass:
myClass * POINTER_32 p = (myClass * POINTER_32)p_x32;
The trouble is that calling a method on 'p' crashes with memory violation. Indeed, under VS debugger I can see that 'p' members are not in order, i.e. the values are bogus. Digging further I found this link
Where the author says: "A handle or pointer cannot be serialized, it is only valid in the process that created it". As said above, apparently the pointer can be serialized (I used INT_PTR) but I wonder if "it is only valid in the process that created it" part is correct.
Thanks in advance.
The documentation is correct - the pointer is only valid in the originating process because the pointers are interpreted relative to the process's memory space. Therefore you cannot pass a pointer between processes and dereference it. You must serialize (deep copy) the actual data and transfer it to the other process.
The exception is you can setup specific "shared memory" spaces between the processes on Windows. Even then though the pointer values themselves are not guaranteed to be identical.
Each process has it's own virtual memory address space. If you pass pointers across process boundaries, they have a completely different meaning in the target process.

How to combine multiple struct BIOs into a single struct request?

I'm working on Linux kernel version 2.6.39.1, and am developing a block device driver. In this regard, I want to combine multiple struct bios into a single struct request, which is then added to the request_queue for processing by the device driver, namely -- scsi_request_fn().
I tried using the ->bi_next field of struct bio to link multiple struct bios that I have composed, thereby creating a linked list of struct bios. When I call submit_bio() to submit a bio to the block device layer for I/O, this BUG_ON() is triggered because the code expects bio->bi_next to be NULL.
Is there a way to link several struct bios into a single struct request before sending it to lower layers for servicing?
I'm not sure how to string multiple struct bio together, but you might want to take a look at the "task collector" implementation in libsas and the aic94xx driver for an alternate approach. There isn't much documentation, but the libsas documentation describes it as
Some hardware (e.g. aic94xx) has the capability to DMA more
than one task at a time (interrupt) from host memory. Task
Collector Mode is an optional feature for HAs which support
this in their hardware. (Again, it is completely optional
even if your hardware supports it.)
In Task Collector Mode, the SAS Layer would do natural
coalescing of tasks and at the appropriate moment it would
call your driver to DMA more than one task in a single HA
interrupt. DMBS may want to use this by insmod/modprobe
setting the lldd_max_execute_num to something greater than 1.
Effectively, this lets the block layer (a.k.a. BIO) remain unchanged, but multiple requests are accumulated at the driver layer and submitted together.
Thanks for the reply, #ctuffli. I've decided to use a structure similar to the one described here. Basically, I allocate a struct packet_data which would contain pointers to all struct bios that should be merged to form one single struct bio (and later on, one single struct request). In addition, I store some driver related information as well in this struct packet_data. Next, I allocate a new struct bio (lets call it "merged_bio"), copy all the pages from the list of original BIOs and then make the merged_bio->bi_private point to the struct packet_data. This last hack would allow me to keep track of the list of original BIOs, and also call bio_endio() to end I/O on all individual BIOs once the merged_bio has been successfully transferred.
Not sure if this is the smartest way to do this, but it does what I intended! :^)

Using linked lists with Bison

Suppose my YYSTYPE is a struct with a pointer to the next struct. Can I direct that pointer to YYSTYPE values of other grammar variables, or are their YYSTYPE values local and will disappear after the derivation ends, causing a segfault later on.
YYSTYPE is the type of variables on Yacc's expression stack.
If the pointers you create are to items actually stored in Yacc's stack, which would typically be done by referencing $1 (or &$1 to get the pointer), then you are indeed pointing to data that will be released and reused, and are in for a world of hurt (such as segmentation faults if you're lucky; confusion and mishandled information if you're unlucky).
If the pointers you create are to items of type YYSTYPE that you manage, then of course there is no problem; you manage their duration, not Yacc.
Copy anything you need from things like $1 into your own storage.

Resources