Kernel module's parameters in sysfs - quick reaction for changes - c

Is it possible to notify the module when one of it's sys files was changed? My task is to do a file which controls size of buffer inside the module, I want to resize the buffer when the value in the file is changed.
My other idea (if I can't notify the module) was to check the previous value each time the module is used and then resize the buffer.

Isn't this the purpose of Sysfs?
When you create a kobject and give it a representation in Sysfs (which is a directory), you then create attributes for that object which will become files in that directory. You provide a store and a show callback to the kobject, which are basically equivalents of resp. write and read.
store is what you want here. It looks like this:
ssize_t (*store)(struct kobject *kobj, struct attribute *attr,
const char *buffer, size_t size);
You receive size bytes within buffer as soon as the virtual file is written in user land.
Have a look at this module which does it (taken from here):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
struct my_attr {
struct attribute attr;
int value;
};
static struct my_attr my_first = {
.attr.name="first",
.attr.mode = 0644,
.value = 1,
};
static struct my_attr my_second = {
.attr.name="second",
.attr.mode = 0644,
.value = 2,
};
static struct attribute * myattr[] = {
&my_first.attr,
&my_second.attr,
NULL
};
static ssize_t default_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
return scnprintf(buf, PAGE_SIZE, "%d\n", a->value);
}
static ssize_t default_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t len)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
sscanf(buf, "%d", &a->value);
return sizeof(int);
}
static struct sysfs_ops myops = {
.show = default_show,
.store = default_store,
};
static struct kobj_type mytype = {
.sysfs_ops = &myops,
.default_attrs = myattr,
};
struct kobject *mykobj;
static int __init sysfsexample_module_init(void)
{
int err = -1;
mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL);
if (mykobj) {
kobject_init(mykobj, &mytype);
if (kobject_add(mykobj, NULL, "%s", "sysfs_sample")) {
err = -1;
printk("Sysfs creation failed\n");
kobject_put(mykobj);
mykobj = NULL;
}
err = 0;
}
return err;
}
static void __exit sysfsexample_module_exit(void)
{
if (mykobj) {
kobject_put(mykobj);
kfree(mykobj);
}
}
module_init(sysfsexample_module_init);
module_exit(sysfsexample_module_exit);
MODULE_LICENSE("GPL");
Also: you might want to output the buffer size to the user when the entry is read. This is usually the way of doing it. Also make sure the information (read and written) is in a human-readable format to keep up with the Unix philosophy.
Update: see this recent interesting article about Sysfs file creation written by Greg Kroah-Hartman, one of the top kernel developers.

Related

A string inside a struct is being overwritten and I can't tell why

I've had this problem for the last two hours and I can't understand what's happening. I expect it to print this
ROBCO INDUSTRIES UNIFIED OPERATING SYSTEM
COPYRIGHT 2075-2077 ROBCO INDUSTRIES
-Server 6-
But instead it displays this
ROBCO INDUSTRIES UNIFIED OPERATING SYSTEM
COPYRIGHT Testing testing 123
I have no clue why this is happening but hopefully you will be able to help me. (If you want to know what this is for it's a Fallout Terminal Emulator). Just ask if you nned any more details. Thanks!
It's being compiled like this if you need to know
gcc test.c configParser.c -lconfig
test.c
#include <stdio.h>
#include "robco.h"
int main() {
struct config_struct config;
config = getConfig("test_config.cfg");
printf("%s", config.banner);
return 0;
}
configParser.c
#include <stdio.h>
#include <libconfig.h>
#include <string.h>
#include "robco.h"
int cap(int num, int cap) {
return (num > cap) ? cap : num;
}
struct config_struct getConfig(const char *filename)
{
config_t cfg;
config_setting_t *root, *menu, *options, *option;
// Read the file. If there is an error, report it and exit.
config_init(&cfg);
config_read_file(&cfg, filename);
root = config_root_setting(&cfg);
// Get the banner from the config file if it exists, if not use the default set in robco.h
const char *banner;
if (! config_setting_lookup_string(root, "banner", &banner) ) {
banner = DEFAULT_BANNER;
}
// Create the config struct and fill in the banner
struct config_struct config;
strcpy(config.banner, banner);
// Get the menu
menu = config_lookup(&cfg, "menu");
// If it can't fetch the menu for any reason return -1
if(! menu) {
return;
}
// Get the options
options = config_setting_get_member(menu, "options");
if(! options) {
return;
}
// Get number of options
config.menu.num_options = cap(config_setting_length(options), MAX_OPTIONS);
// Get the title. If it doesn't exist use the default
const char *title;
if (! config_setting_lookup_string(menu, "title", &title) ) {
title = DEFAULT_TITLE;
}
strcpy(config.menu.title, title);
// Loop through and get the text for all the options
for(int i = 0; i < config.menu.num_options; ++i)
{
option = config_setting_get_elem(options, i);
const char *text;
config_setting_lookup_string(option, "text", &text);
strcpy(config.menu.options[i].text, text);
}
return config;
}
robco.h
#define MAX_STRING_LENGTH 52
#define MAX_OPTIONS 5
#define DEFAULT_BANNER "ROBCO INDUSTRIES UNIFIED OPERATING SYSTEM\nCOPYRIGHT 2075-2077 ROBCO INDUSTRIES\n-Server 6-"
#define DEFAULT_TITLE "-=- TEST TITLE -=-"
struct option_struct {
char text[MAX_STRING_LENGTH];
};
struct menu_struct {
struct option_struct options[MAX_OPTIONS];
int num_options;
int selected;
char title[MAX_STRING_LENGTH];
};
struct config_struct {
char banner[MAX_STRING_LENGTH];
struct menu_struct menu;
};
void printCenter(char msg[], int startRow);
void printMenuOption(char msg[], int startRow);
void drawMenu(struct menu_struct menu, int startRow);
void init_graphics();
struct config_struct getConfig(const char *filename);
test_config.cfg
menu: {
title: "Super Secret Valve Control Panel";
options: (
{
text: "Testing testing 123";
},
{
text: "THE SUN IS A DEADLY LASER";
}
);
};
I bet your MAX_STRING_LENGTH is too little and that you don't test for lengths. You must use strncpy and then don't forget to set the terminating null, which strncpy doesn't do.
You must also turn the warnings of your compiler on. I see struct config_struct getConfig but in that function you do some returns without a return value. The compiler should warn about that.
Your default banner text is longer than MAX_STRING_LENGTH, so when you copy it to config.banner you write past the end of the array, leading to undefined behavior.
Make sure all your buffers are large enough to store the longest string you expect (accounting for the terminator), or truncate strings to the length of the buffer, or use dynamic memory and size the buffers as you need to.

Custom PCI driver fails to probe device

After registering a PCI driver by calling pci_register_driver() within the init_module function, the driver is supposed to take control of any PCI devices which do not already have drivers, assuming that the devices match the vendor ID, device ID, etc. as specified in the struct pci_device_id.
I would like to force the Ethernet Controller to use my driver, just for the sake of experimentation (e.g. reading configuration bytes). On a Virtualbox guest (Mint, kernel 3.13.0), I blacklisted the Ethernet Controller's driver, ran update-initramfs -u, and rebooted. This successfully disassociated the default driver from the Controller, as the driver no longer showed up in the output of lspci -k.
However, when I loaded the module, a few devices that had previously been missing showed up in the output of lspci -k (with my driver controlling them), but the Ethernet Controller still lacked a "Kernel driver in use: " line. How can I make my module recognize and own the Controller?
Note that I used PCI_ANY_ID for the vendor and device fields, and left the other fields of the struct pci_device_id uninitialized. So I would expect the driver to probe any device which currently lacks a driver.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h>
MODULE_LICENSE("GPL");
int init_module(void);
void cleanup_module(void);
static int pci_probe(struct pci_dev *, const struct pci_device_id *);
static void pci_remove(struct pci_dev *dev);
#define SUCCESS 0
#define FAILURE -1
static struct pci_device_id my_pci_id = {
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID
};
static struct pci_driver my_pci_driver = {
.name = "kmod_driver",
.id_table = &my_pci_id,
.probe = pci_probe,
.remove = pci_remove
};
int init_module(void)
{
return pci_register_driver(&my_pci_driver);
}
void cleanup_module(void)
{
pci_unregister_driver(&my_pci_driver);
}
static int pci_probe(struct pci_dev *dev, const pci_device_id *id)
{
int enableStatus = pci_enable_device(dev);
.....
return SUCCESS;
}
static void pci_remove(struct pci_dev *dev)
{
return;
}
You also need to include the subvendor and subdevice fields (setting them to PCI_ANY_ID as well). The matching function is this:
static inline const struct pci_device_id *
pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
{
if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
(id->device == PCI_ANY_ID || id->device == dev->device) &&
(id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
(id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
!((id->class ^ dev->class) & id->class_mask))
return id;
return NULL;
}
You can use the PCI_DEVICE macro to supply the appropriate sub members:
static struct pci_device_id my_pci_id = {
PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID),
};

Overload symbols of running process (LD_PRELOAD attachment)

I'm working on a heap profiler for Linux, called heaptrack. Currently, I rely on LD_PRELOAD to overload various (de-)allocation functions, and that works extremely well.
Now I would like to extend the tool to allow runtime attaching to an existing process, which was started without LD_PRELOADing my tool. I can dlopen my library via GDB just fine, but that won't overwrite malloc etc. I think, this is because at that point the linker already resolved the position dependent code of the already running process - correct?
So what do I do instead to overload malloc and friends?
I am not proficient with assembler code. From what I've read so far, I guess I'll somehow have to patch malloc and the other functions, such that they first call back to my trace function and then continue with their actual implementation? Is that correct? How do I do that?
I hope there are existing tools out there, or that I can leverage GDB/ptrace for that.
Just for the lulz, another solution without ptracing your own process or touching a single line of assembly or playing around with /proc. You only have to load the library in the context of the process and let the magic happen.
The solution I propose is to use the constructor feature (brought from C++ to C by gcc) to run some code when a library is loaded. Then this library just patch the GOT (Global Offset Table) entry for malloc. The GOT stores the real addresses for the library functions so that the name resolution happen only once. To patch the GOT you have to play around with the ELF structures (see man 5 elf). And Linux is kind enough to give you the aux vector (see man 3 getauxval) that tells you where to find in memory the program headers of the current program. However, better interface is provided by dl_iterate_phdr, which is used below.
Here is an example code of library that does exactly this when the init function is called. Although the same could probably be achieved with a gdb script.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/auxv.h>
#include <elf.h>
#include <link.h>
#include <sys/mman.h>
struct strtab {
char *tab;
ElfW(Xword) size;
};
struct jmpreltab {
ElfW(Rela) *tab;
ElfW(Xword) size;
};
struct symtab {
ElfW(Sym) *tab;
ElfW(Xword) entsz;
};
/* Backup of the real malloc function */
static void *(*realmalloc)(size_t) = NULL;
/* My local versions of the malloc functions */
static void *mymalloc(size_t size);
/*************/
/* ELF stuff */
/*************/
static const ElfW(Phdr) *get_phdr_dynamic(const ElfW(Phdr) *phdr,
uint16_t phnum, uint16_t phentsize) {
int i;
for (i = 0; i < phnum; i++) {
if (phdr->p_type == PT_DYNAMIC)
return phdr;
phdr = (ElfW(Phdr) *)((char *)phdr + phentsize);
}
return NULL;
}
static const ElfW(Dyn) *get_dynentry(ElfW(Addr) base, const ElfW(Phdr) *pdyn,
uint32_t type) {
ElfW(Dyn) *dyn;
for (dyn = (ElfW(Dyn) *)(base + pdyn->p_vaddr); dyn->d_tag; dyn++) {
if (dyn->d_tag == type)
return dyn;
}
return NULL;
}
static struct jmpreltab get_jmprel(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct jmpreltab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_JMPREL);
table.tab = (dyn == NULL) ? NULL : (ElfW(Rela) *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_PLTRELSZ);
table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static struct symtab get_symtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct symtab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_SYMTAB);
table.tab = (dyn == NULL) ? NULL : (ElfW(Sym) *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_SYMENT);
table.entsz = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static struct strtab get_strtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct strtab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_STRTAB);
table.tab = (dyn == NULL) ? NULL : (char *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_STRSZ);
table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static void *get_got_entry(ElfW(Addr) base, struct jmpreltab jmprel,
struct symtab symtab, struct strtab strtab, const char *symname) {
ElfW(Rela) *rela;
ElfW(Rela) *relaend;
relaend = (ElfW(Rela) *)((char *)jmprel.tab + jmprel.size);
for (rela = jmprel.tab; rela < relaend; rela++) {
uint32_t relsymidx;
char *relsymname;
relsymidx = ELF64_R_SYM(rela->r_info);
relsymname = strtab.tab + symtab.tab[relsymidx].st_name;
if (strcmp(symname, relsymname) == 0)
return (void *)(base + rela->r_offset);
}
return NULL;
}
static void patch_got(ElfW(Addr) base, const ElfW(Phdr) *phdr, int16_t phnum,
int16_t phentsize) {
const ElfW(Phdr) *dphdr;
struct jmpreltab jmprel;
struct symtab symtab;
struct strtab strtab;
void *(**mallocgot)(size_t);
dphdr = get_phdr_dynamic(phdr, phnum, phentsize);
jmprel = get_jmprel(base, dphdr);
symtab = get_symtab(base, dphdr);
strtab = get_strtab(base, dphdr);
mallocgot = get_got_entry(base, jmprel, symtab, strtab, "malloc");
/* Replace the pointer with our version. */
if (mallocgot != NULL) {
/* Quick & dirty hack for some programs that need it. */
/* Should check the returned value. */
void *page = (void *)((intptr_t)mallocgot & ~(0x1000 - 1));
mprotect(page, 0x1000, PROT_READ | PROT_WRITE);
*mallocgot = mymalloc;
}
}
static int callback(struct dl_phdr_info *info, size_t size, void *data) {
uint16_t phentsize;
data = data;
size = size;
printf("Patching GOT entry of \"%s\"\n", info->dlpi_name);
phentsize = getauxval(AT_PHENT);
patch_got(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, phentsize);
return 0;
}
/*****************/
/* Init function */
/*****************/
__attribute__((constructor)) static void init(void) {
realmalloc = malloc;
dl_iterate_phdr(callback, NULL);
}
/*********************************************/
/* Here come the malloc function and sisters */
/*********************************************/
static void *mymalloc(size_t size) {
printf("hello from my malloc\n");
return realmalloc(size);
}
And an example program that just loads the library between two malloc calls.
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
void loadmymalloc(void) {
/* Should check return value. */
dlopen("./mymalloc.so", RTLD_LAZY);
}
int main(void) {
void *ptr;
ptr = malloc(42);
printf("malloc returned: %p\n", ptr);
loadmymalloc();
ptr = malloc(42);
printf("malloc returned: %p\n", ptr);
return EXIT_SUCCESS;
}
The call to mprotect is usually useless. However I found that gvim (which is compiled as a shared object) needs it. If you also want to catch the references to malloc as pointers (which may allow to later call the real function and bypass yours), you can apply the very same process to the symbol table pointed to by the DT_RELA dynamic entry.
If the constructor feature is not available for you, all you have to do is resolve the init symbol from the newly loaded library and call it.
Note that you may also want to replace dlopen so that libraries loaded after yours gets patched as well. Which may happen if you load your library quite early or if the application has dynamically loaded plugins.
This can not be done without tweaking with assembler a bit. Basically, you will have to do what gdb and ltrace do: find malloc and friends virtual addresses in the process image and put breakpoints at their entry. This process usually involves temporary rewriting the executable code, as you need to replace normal instructions with "trap" ones (such as int 3 on x86).
If you want to avoid doing this yourself, there exists linkable wrapper around gdb (libgdb) or you can build ltrace as a library (libltrace). As ltrace is much smaller, and the library variety of it is available out of the box, it will probably allow you to do what you want at lower effort.
For example, here's the best part of the "main.c" file from the ltrace package:
int
main(int argc, char *argv[]) {
ltrace_init(argc, argv);
/*
ltrace_add_callback(callback_call, EVENT_SYSCALL);
ltrace_add_callback(callback_ret, EVENT_SYSRET);
ltrace_add_callback(endcallback, EVENT_EXIT);
But you would probably need EVENT_LIBCALL and EVENT_LIBRET
*/
ltrace_main();
return 0;
}
http://anonscm.debian.org/cgit/collab-maint/ltrace.git/tree/?id=0.7.3

pthread_mutex_lock gets stuck

The revelant code may be found here: http://pastebin.com/VbhtQckm
The problem is at line
85. pthread_mutex_lock(ID_retrieval_pool->info->lock);
I'm running the server and it's getting stuck at lock. The memory is allocated, I'm initializing the mutex and it's the only thread who's owning that shared memory.
I did debug with GDB and Valgrind using helgrind tool but did not find any clue.
Possible problems which think may cause this:
mutex is not being initialized (I'm using a block is shared memory which I'm initializing as a mutex);
deadlock? in the man page https://www.sourceware.org/pthreads-
win32/manual/pthread_mutex_init.html says this can cause this;
Please note that this code is for learning purpose.
Edit, the code is:
// common_header.h + common_header.c
#ifndef DATA_TYPES_H
#define DATA_TYPES_H
#include <pthread.h>
#include <errno.h>
#define RETREIVE_ID_KEY 1
typedef enum {
SHM_State_None,
SHM_State_ID_Available,
SHM_State_ID_Not_Available,
} SHM_State;
typedef struct {
pthread_mutex_t *lock; // locked if any thread is modifying data
SHM_State state;
} data_state;
typedef int shmid_t;
typedef struct data_pool {
data_state *info;
shmid_t shm_id;
} data_pool;
// other data structures
extern data_state * data_state_initialize_by_setting_address(void *address)
{
data_state *data = (data_state *)address;
data->lock = (pthread_mutex_t *)address;
pthread_mutex_init(data->lock, NULL);
data->state = SHM_State_None;
return data;
}
extern data_pool * data_pool_initialize_by_setting_address(void *address)
{
data_pool *data = (data_pool *)address;
data->info = data_state_initialize_by_setting_address(address);
data->shm_id = 0; // invalid though, the structure's client has to set a valid one
return data;
}
// other initialization functions
#endif // DATA_TYPES_H
///----------------------------------------------------------------------------------------\\\
// main.c -- Server
#include "common_header.h"
#define SHM_INVALID_ADDRESS (void *)-1
#define SHMGET_RW_FLAGS 0666
#define SHMAT_RW_FLAGS 0
bool initialize_data();
static data_pool *ID_retrieval_pool = NULL;
int main(int argc, char *argv[])
{
if (!initialize_data()) {
return EXIT_FAILURE;
}
// Do other stuff
return EXIT_SUCCESS;
}
bool initialize_data()
{
// some irrelevant initialization code
shmid_t shm_ID = shmget(RETREIVE_ID_KEY,
sizeof(data_pool),
IPC_CREAT | SHMGET_RW_FLAGS);
void *shm_address = shmat(shm_ID, NULL, SHMAT_RW_FLAGS);
if (shm_address == SHM_INVALID_ADDRESS) {
return false;
}
ID_retrieval_pool = data_pool_initialize_by_setting_address(shm_address);
pthread_mutex_lock(ID_retrieval_pool->info->lock);
ID_retrieval_pool->shm_id = get_shared_ID();
ID_retrieval_pool->info->state = SHM_State_ID_Available;
pthread_mutex_unlock(ID_retrieval_pool->info->lock);
// other initialization code
return true;
}
You have an interesting and incorrect way of initializing the mutex:
data->lock = (pthread_mutex_t *)address; /* address == &data */
pthread_mutex_init(data->lock, NULL);
In your code address is the address of the outer struct: this does not actually allocate a usable block of memory.
I suggest you just make the mutex non-pointer and then initialize it:
/* In the struct. */
pthread_mutex_t lock;
/* In your function. */
pthread_mutex_init(&data->lock, NULL);

i2c device driver. System down when try to send data

I trying to write i2c device driver and all is gone fine:
device detected successfully
device in sysfs class with value attribute created too
then I init a hrtimer and start it
I am trying to send data from store function of attribute and it's work fine
but when I trying to send data from hrtimer callback function my system going down. Here is some code (i was remove incudes, defines and non-important code):
led-indicator.h
struct timer_job {
ktime_t period;
struct hrtimer timer;
...
};
struct ledindicator {
struct i2c_client *client;
struct device *device;
struct timer_job job;
int value;
};
led-indicator.c
include "led-indicator.h"
static struct ledindicator *li;
// device attribute [value] store function
static ssize_t value_store(struct device *device, struct device_attribute *attr, const char *buffer, size_t count)
{
int val;
kstrtoint(buffer, 10, &val);
li->value = val;
i2c_smbus_write_byte_data(li->client, 0xBA, 0x02); // this is work fine
LOG("New value is %d \n", li->value);
return count;
}
//timer callback, fire each timer_job.period
static enum hrtimer_restart send_next(struct hrtimer * tim) {
long start = jiffies;
struct timer_job *job = container_of(tim, struct timer_job, timer);
i2c_smbus_write_byte_data(li->client, 0xBA, 0x02); // this cause system fault
hrtimer_forward_now(tim, job->period);
return HRTIMER_RESTART;
}
//when device is detected I am create sysfs device and init timer.
enter code here//Also I am crate led_indicator structure and save i2c_client pointer for future use
static int li_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
li = kmalloc(sizeof(*li), GFP_KERNEL);
li->client = client;
li->value = 0;
...
// here creating device and attribute for it
...
hrtimer_init(&(li->job.timer), CLOCK_MONOTONIC, HRTIMER_MODE_REL);
li->job.timer.function = send_next;
li->job.period = ktime_set(1, 0); //1 sec
hrtimer_start(&(li->job.timer), li->job.period, HRTIMER_MODE_REL);
return ret;
}
What is wrong?

Resources