Why Linux kernel LSM_HOOK macro is defined with many parameters? - c

In Linux kernel, the LSM_HOOK usage is like:
LSM_HOOK(int, 0, binder_set_context_mgr, const struct cred *mgr)
LSM_HOOK(int, 0, binder_transaction, const struct cred *from,
const struct cred *to)
LSM_HOOK(int, 0, binder_transfer_binder, const struct cred *from,
const struct cred *to)
LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
const struct cred *to, struct file *file)
LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
unsigned int mode)
LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
LSM_HOOK(int, 0, capget, struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old,
const kernel_cap_t *effective, const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
LSM_HOOK(int, 0, capable, const struct cred *cred, struct user_namespace *ns,
int cap, unsigned int opts)
The LSM_HOOK is defined as:
struct security_hook_heads {
#define LSM_HOOK(RET, DEFAULT, NAME, ...) struct hlist_head NAME;
#include "lsm_hook_defs.h"
#undef LSM_HOOK
} __randomize_layout;
In this situation, parameters except for NAME are all discarded. I am curious why the macro expansion needs so many arguments like above. Thanks!

This is because the macro LSM_HOOK has different implementations in various subsystem or modules, as you can see, in the struct, it undef the macro immediately after usage, so in this particular module, the remaining arguments are not needed and ignored.
But let's look at another module, e.g. in bpf_lsm.h, it defines LSM_HOOK like this:
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
RET bpf_lsm_##NAME(__VA_ARGS__);
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
where the extra arguments are passed into the bpf_lsm_... functions.
So this kind of macro provides extensibility and flexibility among different modules.

Related

How to get the minor number of a file in a Linux driver?

Is there a better solution to get the minor number?
Can I avoid checking kernel version?
static long unlocked_ioctl(struct file *f, unsigned int o, unsigned long d)
{
#if KERNEL_VERSION(3, 18, 0) > LINUX_VERSION_CODE
struct inode* inode = f->f_dentry->d_inode;
#else
struct inode* inode = f->f_path.dentry->d_inode;
#endif
int minor = iminor(inode);
}
Yes, there's a better way: don't bother looking at the dentry when what you want is right there as a field of struct file.
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode; // <-- here's your inode
// ...
}
You can either access f->f_inode directly or use the file_inode() function, this way you can also avoid kernel version checks.
static long unlocked_ioctl(struct file *f, unsigned int o, unsigned long d)
{
int minor = iminor(file_inode(f));
// ...
}
As an addendum to Marco Bonelli's answer, file_inode() was added in the 3.9 kernel, so if earlier kernel versions need to be supported, some kernel compatibility code needs to be added. I use something like the following:
/*
* The file_dentry() inline function was added in kernel version 4.6.0.
* Emulate it for earlier kernels.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
static inline struct dentry *kcompat_file_dentry(const struct file *f)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
return f->f_dentry;
#else
return f->f_path.dentry;
#endif
}
#undef file_dentry
#define file_dentry(f) kcompat_file_dentry(f)
#endif
/*
* The file_inode() inline function was added in kernel 3.9.0.
* Emulate it for earlier kernels.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
static inline struct inode *kcompat_file_inode(struct file *f)
{
return file_dentry(f)->d_inode;
}
#undef file_inode
#define file_inode(f) kcompat_file_inode(f)
#endif

C header file The chicken or The egg problem

I was developing a c program (a compositor) and I suddenly ran into a problem.
Here is the problem:
I have two header files server.h and seat.h which consists of one struct each.
server.h
typedef struct
{
const char *socket;
struct wl_display *wl_display;
struct wl_event_loop *wl_event_loop;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_compositor *compositor;
struct wlr_output_layout *output_layout;
Seat *seat; // the seat struct from seat.h
struct wl_list outputs;
struct wl_listener output_listener;
} Server;
bool init_server(Server *server);
void run_server(Server *server);
void destroy_server(Server *server);
seat.h
typedef struct
{
Server *server; // the server struct from server.h
struct wlr_seat *wlr_seat;
struct wl_listener input_listener;
struct wl_listener destroy_seat;
} Seat;
Seat *create_seat(Server *server);
void handle_new_input(struct wl_listener *listener, void *data);
void destroy_seat(struct wl_listener *listener, void *data);
The main problem is that it creates a loop of header files so when I compile it causes error.
I have read about the problem in here C header file loops. And I tried this it worked in the case of struct but when I call the create_seat() function it is telling that the type is mismatched. In my case I'm using typedef too so it's a little bit confusing.
Since the actual code is not good to run on any machines (because it need dependencies and so on) please use this code as the reference, this explains my actual problem.
I uses meson build system. If I compile the program using ninja it ends in an infinite loop.
Here's the code:
main.c
#include <stdio.h>
#include "server.h"
#include "seat.h"
int main()
{
Server server;
server.id=10;
Seat seat;
seat.id=20;
server.seat=seat;
seat.server=server;
printSeatId(server);
printServerId(seat);
return 0;
}
server.h
#include "seat.h"
typedef struct
{
Seat seat;
int id;
} Server;
void printSeatId(Server s);
seat.h
#include "server.h"
typedef struct
{
Server server;
int id;
} Seat;
void printServerId(Seat s);
server.c
#include <stdio.h>
#include "server.h"
void printSeatId(Server s)
{
printf("%d",s.seat.id);
}
seat.c
#include <stdio.h>
#include "seat.h"
void printServerId(Seat s)
{
printf("%d",s.server.id);
}
meson.build - in src folder
sources = files(
'main.c',
'server.c',
'seat.c'
)
executable(
'sample',
sources,
include_directories: [inc],
install: false,
)
meson.build in project folder
project(
'sample',
'c',
version: '1.0.0',
meson_version: '>=0.56.0',
default_options: ['c_std=c11','warning_level=2'],
)
add_project_arguments(
[
'-DWLR_USE_UNSTABLE',
'-Wno-unused',
'-Wno-unused-parameter',
'-Wno-missing-braces',
'-Wundef',
'-Wvla',
'-Werror',
'-DPACKAGE_VERSION="' + meson.project_version() + '"',
],
language: 'c',
)
cc = meson.get_compiler('c')
inc = include_directories('include')
subdir('src')
Here is the directory structure :
<project_folder>
|--->src
| |--->server.c
| |--->seat.c
| |--->meson.build
|
|--->include
| |--->server.h
| |--->seat.h
|
|--->meson.build
I have given the same directory structure of the original project.
The way to resolve the conflict is by adding a forward declaration of an incomplete structure type. The struct type used in the forward declaration needs a tag and the same tag is used for the complete declaration of the same type. For symmetry, it makes sense to add forward declarations of both the Seat and Server structure types. The typedef type definitions can be moved to one or more new header files that are included by the seat.h and server.h header files. The header files need to define guard macros to avoid multiple definition conflicts.
For example:
seat_server_t.h
#ifndef SEAT_SERVER_T_H__INCLUDED
#define SEAT_SERVER_T_H__INCLUDED
typedef struct Seat_s Seat;
typedef struct Server_s Server;
#endif
seat.h
#ifndef SEAT_H__INCLUDED
#define SEAT_H__INCLUDED
#include "seat_server_t.h"
/* Other includes for struct wl_listener, etc. here... */
struct Seat_s
{
Server *server;
struct wlr_seat *wlr_seat;
struct wl_listener input_listener;
struct wl_listener destroy_seat;
};
Seat *create_seat(Server *server);
void handle_new_input(struct wl_listener *listener, void *data);
void destroy_seat(struct wl_listener *listener, void *data);
#endif
server.h
#ifndef SERVER_H__INCLUDED
#define SERVER_H__INCLUDED
#include "seat_server_t.h"
/* Other #include's for struct wl_display, etc. here... */
struct Server_s
{
const char *socket;
struct wl_display *wl_display;
struct wl_event_loop *wl_event_loop;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_compositor *compositor;
struct wlr_output_layout *output_layout;
Seat *seat; // the seat struct from seat.h
struct wl_list outputs;
struct wl_listener output_listener;
};
bool init_server(Server *server);
void run_server(Server *server);
void destroy_server(Server *server);
#endif
The new header file seat_server_t.h does not need to be included directly by the .c files such as main.c. It can be treated as for internal only by the other header files, seat.h and server.h. It could also be split into two separate header files (one for each typedef) if desired.
If you just need a pointer to the other struct, you can forward-declare that one, like this:
typedef struct Server Server;
typedef struct
{
Server *server; // the server struct from server.h
struct wlr_seat *wlr_seat;
struct wl_listener input_listener;
struct wl_listener destroy_seat;
} Seat;

nfhook (netfilter) error: assignment from incompatible pointer type

I have seen this page with similar error message: Nf_hook_ops returns incompatible pointer when assigning to hook_func -C -Linux -Netfilter
However, it didn't give a clear answer to how to solve the issue. The author for that question says that he found out his netfilter.h is located elsewhere which caused the trouble, but for me I found out that all the four files included are in the correct directory (usr/src/linux-headers-4.8.0-22-generic/include/linux in my case).
Following is my code which should help clarify better.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
static struct nf_hook_ops nfho;
unsigned int hook_func_incoming(unsigned int hooknum, struct sk_buff *sskb,
const struct net_device *in, const struct net_device *out, int (*okfn)
(struct sk_buff *)){
return NF_DROP;
}
int init_module(){
nfho.hook = hook_func_incoming;
nfho.hooknum = NF_INET_PRE_ROUTING;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfho);
printk(KERN_INFO "SIMPLE FIREWALL LOADED\n");
return 0;
}
The exact error message is this:
error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
nfho.hook = hook_func_incoming;
^
cc1: some warnings being treated as errors
Please let me know what I should do to be able to compile my netfilter, any help is appreciated!
In the (IMHO) latest (released) netfilter version, nf_hookfn (the base type of nf_hook_ops.hook) is defined as follows:
typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);
Your function hook_func_incoming does not match this signature, you should adopt it.
Third parameter is this data structure. In the new definition of the hook function, they wanted to combine the old parameters in a single data structure. So, if you need the out device, you can get it from this state parameter.
struct nf_hook_state {
unsigned int hook;
int thresh;
u_int8_t pf;
struct net_device *in;
struct net_device *out;
struct sock *sk;
struct net *net;
struct nf_hook_entry __rcu *hook_entries;
int (*okfn)(struct net *, struct sock *, struct sk_buff *);
};
priv is a field inside the struct nf_hook_ops. You can set it to any value in your own module and access it in your hook function.
struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
void *priv;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};

error: implicit declaration of function 'create_proc_read_entry' [-Werror=implicit-function-declaration]

I'm trying to compile a kernel module on kernel 3.13 and I get this error:
error: implicit declaration of function 'create_proc_read_entry' [-Werror=implicit-function-declaration]
I google it and did not found any response. Here is the part of the code which refers to this error:
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
proc = proc_net_create(KAODV_QUEUE_PROC_FS_NAME, 0, kaodv_queue_get_info);
#else
proc = create_proc_read_entry(KAODV_QUEUE_PROC_FS_NAME, 0, init_net.proc_net, kaodv_queue_get_info, NULL);
#endif
if (!proc) {
printk(KERN_ERR "kaodv_queue: failed to create proc entry\n");
return -1;
}
Can I get help ? I really don't know what is wrong. It might be the kernel 3.13 which needs a patch. I read somewhere (on KERNEL 3.10) that the kernel needs patch. Can anyone show me where can I get the 3.13 kernel patch to eventually fix the problem. Thanks
The error is because you are not including explicitly the header that declares the function and the compiler is 'including' implicitily for you and this throws a warning. The flag '-Werror' is making the compiler treats the warning as an error. Try adding: #include <linux/proc_fs.h>
Also: create_proc_read_entry is a deprecated function.
Take a look at: https://lkml.org/lkml/2013/4/11/215
in Linux 3.9
static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
umode_t mode,
struct proc_dir_entry *base,
read_proc_t *read_proc,
void * data
) { return NULL; }
http://lxr.free-electrons.com/source/include/linux/proc_fs.h?v=3.9
in Linux 3.10
static inline struct proc_dir_entry *proc_create(const char *name,
umode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops
)
http://lxr.free-electrons.com/source/include/linux/proc_fs.h?v=3.10
So change create_proc_read_entry() to proc_create(), and change the 5 parameters to 4 parameters. It then works.
In your linux version 3.13 create_proc_read_entry this method has been deleted,instead using proc_create or proc_create_data.You can use this API
struct proc_dir_entry *proc_create_data(const char *, umode_t,
struct proc_dir_entry *,
const struct file_operations *,
void *);
static inline struct proc_dir_entry *proc_create(
const char *name, umode_t mode, struct proc_dir_entry *parent,
const struct file_operations *proc_fops);

How can I use a sysfs kobject as a global variable?

I would like to use an user-editable global variable in the linux kernel. Is that possible?
That's what I came up with using the example provided with the source code:
arch/x86/kernel/foo.c
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>
int foo = 12;
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", foo);
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
sscanf(buf, "%du", &foo);
return count;
}
static struct kobj_attribute foo_attribute =
__ATTR(foo, 0666, foo_show, foo_store);
static struct attribute *attrs[] = {
&foo_attribute.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static struct kobject *example_kobj;
static int __init example_init(void)
{
int retval;
example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
if (!example_kobj)
return -ENOMEM;
retval = sysfs_create_group(example_kobj, &attr_group);
if (retval)
kobject_put(example_kobj);
return retval;
}
static void __exit example_exit(void)
{
kobject_put(example_kobj);
}
module_init(example_init);
module_exit(example_exit);
include/linux/foo.h
#ifndef FOO_H
#define FOO_H
extern unsigned int foo;
#endif
arch/x86/randomfile.c
#include <linux/foo.h>
....
int foobar = ( 12 + foo );
....
I get this
error: initializer element is not constant
which makes me realize I must be doing something really wrong, but as much as I search I can't find anything and I can't figure out how to do it from looking at other implementations in the kernel...
Could someone point me to the right direction, possibly with a practical example?
C global variables must be initialized with values known at compile-time. foo isn't.
As a general rule, it's possible to initialize a global integer with enum values, numeric constants, and mathematical operations on either of them. For instance, this is valid:
enum foo_enum
{
foo = 12
};
int foobar = (12 + foo);
But, obviously, you just lost the ability to change foo at runtime.
If there is a function that is called when the kernel module is loaded (is that what the __init-marked function is?), you can do the initialization there.

Resources