I have write a linux driver program.
and the main code is:
struct file_operations dummy_fops = {
open: dummy_open,
release: dummy_release,
read: dummy_read,
write: dummy_write,
};
ssize_t dummy_read (struct file *filp, char __user * buf, size_t count,
loff_t * offset)
{
int c = 0;
pr_info("Nothing to read guy\n");
pr_info("address %p %x", &c, &c); // this is output
return 0;
}
I tapped the command:
make
sudo insmod my_module
sudo cat /dev/my_module
dmesg
the main output is:
[10004.473406] Someone tried to open me
[10004.473425] Nothing to read guy
[10004.473431] address 32ba0e06 eef1ff1c
[10004.473446] Someone closed me
Why the output %p 32ba0e06 and the output %x eef1ff1c is different?
Because the pointers in the Linux kernel are hashed by default for the security reasons. So, taking into account the 32-bit architecture (or the kernel compiled for 32-bit mode) there is no difference between %px and %x. The %px is the special pointer specifier extension that tells “I do want to print the real address”.
PS for 64-bit architecture the %x will print only least 32 bits, so you need to use %lx instead.
Related
I'm stumped, and I need someone to tell me what I'm missing.
I've had to upgrade the U-boot bootloader on my devices, and naturally I've had to make things fit again. And right now I'm trying to get the U-Boot environment variables accessible from Linux/Android.
Long story short, I've created a file /etc/fw_env.config that points to U-Boot's env section in Flash. As documented here: https://elinux.org/U-boot_environment_variables_in_linux
This has not been successful, and so I started adding print statements to the source code to debug my device. I tracked the error down to a get_config() function, which as one could imagine, opens the /etc/fw_env.config and writes the values within to the necessary variables.
I've narrowed it further down to the sscanf() function, which returns 0, as in 0 variables read and/or written. So, as a sanity check, I isolated the function and made my own little program separately from my source code (variable names and structures I kept exactly the same).
/* sscanf example */
#include <stdio.h>
struct envdev_s {
const char *devname; /* Device name */
long long devoff; /* Device offset */
unsigned long env_size; /* environment size */
unsigned long erase_size; /* device erase size */
unsigned long env_sectors; /* number of environment sectors */
unsigned int mtd_type; /* type of the MTD device */
};
static struct envdev_s envdevices[2] = {};
#define DEVNAME(i) envdevices[(i)].devname
#define DEVOFFSET(i) envdevices[(i)].devoff
#define ENVSIZE(i) envdevices[(i)].env_size
#define DEVESIZE(i) envdevices[(i)].erase_size
#define ENVSECTORS(i) envdevices[(i)].env_sectors
#define DEVTYPE(i) envdevices[(i)].mtd_type
int main ()
{
char dump [] = "/dev/mtd1 0xc0000 0x2000 0x2000\n";
char *devname;
int i = 0;
int rc;
printf("I was here in get_config : dump = %s\n", dump);
printf("I was here in get_config : i = %d\n", i);
rc = sscanf(dump, "%ms %lli %lx %lx %lx",
&devname,
&DEVOFFSET(i),
&ENVSIZE(i),
&DEVESIZE(i),
&ENVSECTORS(i));
printf("I was here in get_config : rc = %d\n", rc);
return 0;
}
Also recreated here: http://cpp.sh/5ckms
Now, when I run this independently, it works as I expect it should, particularly outputting:
I was here in get_config : rc = 4
4 being successful, as char dump [] = "/dev/mtd1 0xc0000 0x2000 0x2000\n";
But when I compile this and run it on my device, it returns:
I was here in get_config : rc = 0
Computer says NO! And no other error messages to work with.
I'm obviously missing some fundamental understanding here. Either some permissions, or some setup-variables somewhere, but I wouldn't know in where to start. Could someone please point me in the right direction?
For completeness, I am answering this question based on the help I have received here on StackOverflow:
As stated in the comments, %ms was not added until Android 9, and I was working on Android 6. It still did not produce any compiler errors which is particularly misleading. I ended up using %s, which worked fine.
I have been writing a kernel space device driver that can be read from and written to from user space. The open, read, release operations all work perfectly. The problem I am having is with the user-space code that should access the device driver and and write something to it.
The user-space program writes to two files: 1) to a .txt file (and prints a to the console to let the user know it was completed), and 2) to the device driver (and also prints a text to let the user know it was also completed).
Below is the user-space code in full:
int main() {
FILE *fp;
fp = fopen("./test.txt","w");
fputs("Test\n", fp);
fclose(fp);
printf("Printed to txt\n"); //Prints normally.
fp = fopen("/dev/testchar", "w");
fputs("Test\n", fp);
fclose(fp);
printf("Printed to dev\n"); //Never gets to this point
return 0;
}
When I compile and run the code the program spits out
Printed to txt
and just hangs until ctrl+c is called. It never gets beyond the second fputs().
While monitoring kern.log I see endless calls to write to the device driver.
Here I have extracted relevant code from the device driver:
static char msg[256] = {0};
static struct file_operations fops =
{
.write = dev_write
};
static ssize_t dev_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
{
sprintf(msg, "Input:%s, Chars:%lu\n", buf, len);
printk(KERN_NOTICE "%s\n", msg);
return 0;
}
uname -r: 4.10.0-38-generic
gcc -v: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
My question is: why is the program getting stuck in an infinite loop when writing to the device, and how do I fix it?
Thanks in advance. Any help will be greatly appreciated.
I think the kernel write operation is supposed to return the number of bytes written. You return 0. So the write system call returns to userspace with 0. However, since your userspace code is using stdio, then your userspace code tries the write again, assuming the system call simply didn't write out all the data. If you return the length of the input, then stdio will know all the data was written. Alternatively you can use the write system call directly rather than fputs. Your kernel code will still be incorrect, but your program will terminate.
You can test this using strace and see all the system calls.
This is what I found when I was working on one of my course projects. Below is the C code block to print out the information about an empty pipe which has not connected to any process yet.
{
int pfd[2], nread;
char s[100];
struct stat pipe_info;
if (pipe(pfd) == -1)
{
perror ("pipe");
return (-1);
}
if (fstat (pfd[0], &pipe_info) < 0)
perror ("fstat");
print_info (&pipe_info);
if (fstat (pfd[1], &pipe_info) < 0)
perror ("fstat");
print_info (&pipe_info);
return(0);
}
void print_info (struct stat *pipe_info)
{
printf ("mode %o\n", pipe_info->st_mode);
printf ("inode %d\n", pipe_info->st_ino);
printf ("device %d\n", pipe_info->st_dev);
printf ("minor device %d\n", pipe_info->st_rdev);
printf ("num links %d\n", pipe_info->st_nlink);
printf ("uid %d\n", pipe_info->st_uid);
printf ("gid %d\n", pipe_info->st_gid);
printf ("size %d\n", pipe_info->st_size);
printf ("atime %d\n", pipe_info->st_atime);
printf ("mtime %d\n", pipe_info->st_mtime);
printf ("ctime %d\n", pipe_info->st_ctime);
printf ("block size %d\n", pipe_info->st_blksize);
printf ("block %d\n", pipe_info->st_blocks);
}
I compiled the source code on both a Linux machine and a Solaris OS machine. What I found was that on the Linux machine, the number of links is 1 while on the Solaris OS machine, the number of links for the pipe is 0. I am fairly new to the kernels of both systems and would like to know why the number of links are different on the two systems?
The SunOS 5.10 / Solaris 2.x manual says this about the st_nlink field:
st_nlink This field should be used only by administrative commands.
which I read as "this field has a nonsensical value".
Contrariwise, the value for Linux makes sense: the pipe has a link to the process that created it. I expect that st_nlink would equal 2 once the other side was conected to a (forked) process. The Linux fstat claims POSIX compliance which is good. The Solaris man page I have makes no such claims.
If your underlying question is how can I tell if the farside of a pipe is connected, there are two answers:
Your program should know if it attached the farside
You can try to write the pipe and get some combination of EAGAIN, EWOULDBLOCK, EPIPE, or the SIGPIPE signal.
Option 2 would be problematic if the other side of the pipe is connected. You could work around it if you can create a message that would never be sent by the writer to be rejected by the reader.
I was going to use /dev/random output as a seed for key generation for openssl, then I wrote this small program just to check what I was going to do:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define LEN 128
void uc2hex(char* hex, unsigned char* uc, unsigned short uc_len)
{
FILE* bp=fmemopen(hex,2*uc_len+1,"w");
unsigned short i;
for(i=0;i<uc_len;i++)
{
fprintf(bp,"%02x",uc[i]);
//printf("%02x\n",uc[i]);
//fprintf(bp,"%d-",i);
}
fprintf(bp,"%c",'\0');
fclose(bp);
}
int main()
{
unsigned char buf[LEN];
char str[2*LEN+1];
int fd=open("/dev/random",O_RDONLY);
read(fd,buf,LEN);
uc2hex(str,buf,LEN);
printf("%s\n",str);
close(fd);
return 0;
}
I ran the program some one or two times and everything seemed to work fine, but then I ran it four times again in short sequence and this is the output:
[walter#eM350 ~]$ ./random
0ee08c942ddf901af1278ba8f335b5df8db7cf18e5de2a67ac200f320a7a20e84866f533667a7e66a4572b3bf83d458e6f71f325783f2e3f921868328051f8f296800352cabeaf00000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000
[walter#eM350 ~]$ ./random
1f69a0b931c16f796bbb1345b3f58f17f74e3df600000000bb03400000000000ffffffff00000000880e648aff7f0000a88103b4d67f000000305cb4d67f000030415fb4d67f0000000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000
[walter#eM350 ~]$ ./random
4e8a1715238644a840eb66d9ff7f00002e4e3df600000000bb03400000000000ffffffff00000000a8ec66d9ff7f0000a871a37ad97f00000020fc7ad97f00003031ff7ad97f0000000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000
[walter#eM350 ~]$ ./random
598c57563e8951e6f0173f0cff7f00002e4e3df600000000bb03400000000000ffffffff0000000058193f0cff7f0000a8e1cbda257f0000009024db257f000030a127db257f0000000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000
Theese seem to me everything but 128-bytes random strings, since they are mostly the same. Then, excluding the possibility that the NSA tampered with the linux kernel random-number-generator, I could only guess that this has something to do with the available entropy in my machine, which gets exhausted when I ask too many bytes in sequence. My questions are:
1) Is this guess correct?
2) Assuming 1) is correct, how do I know whether there is enough entropy to generate real random bytes sequence?
From the man page for read:
Upon successful completion, read(), readv(), and pread() return the number of bytes actually read and placed in the buffer. The system guarantees to read the number of bytes requested if the descriptor references a normal file that has that many bytes left before the end-of-file, but in no other case.
Bottom line: check the return value from read and see how many bytes you actually read - there may not have been enough entropy to generate the number of bytes you requested.
int len = read(fd, buf, LEN);
printf("read() returned %d bytes: ", len);
if (len > 0)
{
uc2hex(str, buf, len);
printf("%s\n", str);
}
Test:
$ ./a.out
read() returned 16 bytes: c3d5f6a8ee11ddc16f00a0dea4ef237a
$ ./a.out
read() returned 8 bytes: 24e23c57852a36bb
$ ./a.out
read() returned 16 bytes: 4ead04d1eedb54ee99ab1b25a41e735b
$
As other people have suggested, you need to check the return value for the number of bytes read.
If /dev/random did not have sufficent bytes available, it will have returned fewer.
However, you still use the expected length in your following calls:
uc2hex(str,buf,LEN);
printf("%s\n",str);
So, you are converting and printing uninitialised memory. I am not surprised that subsequent calls then show the same value - since if that memory hasn't been written to between calls, the value wont change.
EDIT: Better would be:
int nBytes=read(fd,buf,LEN);
uc2hex(str,buf,nBytes);
printf("%s\n",str);
I got the following.
my-app.c file:
char username[USERNAME_MAX_LEN] = "\0";
char password[PASSWORD_MAX_LEN] = "\0";
scanf("%s %s", username, password);
printf("username-len: %ld, password-len: %ld\n", strlen(username), strlen(password));
credentials.data file:
jdons f4$dsef35fs
So:
$ ./my-app < credentials.data
username-len: 0, password-len: 0
and:
$ cat credentials.data | ./my-app
username-len: 5, password-len: 10
Why in the both ways the input is handled differently? What is the difference to be so?
What is the right way to use scanf() to be able to handle both cases in the same way?
This line:
scanf("%s %s", username, password);
is inherently unsafe (unless you have complete control over what will appear on your program's standard input). The "%s" format says to read an arbitrarily long sequence of non-whitespace characters. However long the target array is, a sufficiently long word (say, caused by your cat sitting on the keyboard) will overflow it.
You can use a length modifier to limit the size of the input. For example (untested):
scanf("%36s %36s", username, password);
or, better:
scanf("%*s, %*s", USERNAME_MAX_LEN, username, PASSWORD_MAX_LEN, password);
But it's probably better to use fgets() to read an entire line at a time, and then use, say, sscanf() to process the line after you've read it.
And there's a possible problem in your printf call:
printf("username-len: %ld, password-len: %ld\n",
strlen(username),
strlen(password));
strlen() returns a result of type size_t, but "%ld" requires an argument of type long int. If your system supports it, you can use "%zu" to print a value of type size_t, but that's not 100% portable. Or you can convert the size_t value to, say, unsigned long:
printf("username-len: %lu, password-len: %lu\n",
(unsigned long)strlen(username),
(unsigned long)strlen(password));
It's possible, but not very likely, that that could cause non-zero size_t values to be displayed as 0.
Barankin,
Hmmm... that's an interesting behavior.
Both standard input indirection techniques work (as expected) for me...
landkrc#lasun175:/home/user/landkrc/crap
$ cat lg.c
#include <stdio.h>
#include <strings.h>
#define USERNAME_MAX_LEN 36
#define PASSWORD_MAX_LEN 36
int main(int argc, char *argv[])
{
printf("Hello, world\n");
char username[USERNAME_MAX_LEN];
char password[PASSWORD_MAX_LEN];
*username = 0;
*password = 0;
scanf("%s %s", username, password);
printf("username-len: %ld, password-len: %ld\n", strlen(username), strlen(password));
return 0;
}
landkrc#lasun175:/home/user/landkrc/crap
$ cc -V
cc: Sun C 5.8 2005/10/13
usage: cc [ options] files. Use 'cc -flags' for details
landkrc#lasun175:/home/user/landkrc/crap
$ cc -o lg lg.c
landkrc#lasun175:/home/user/landkrc/crap
$ echo '12345678 1234567890
> ' >data.txt
landkrc#lasun175:/home/user/landkrc/crap
$ lg <data.txt
username-len: 8, password-len: 10
landkrc#lasun175:/home/user/landkrc/crap
$ cat data.txt | lg
username-len: 8, password-len: 10
Maybe you just need an end-of-line-character on the end of your credentials.data file?
Cheers. Keith.
Check if your credentials.data contains a a newline character at the end? cat command appends it after the last line of file automatically.
As much as I try, I can't manage to reproduce the same problem as you. I even manually removed the end of line byte from the credentials.data file and it still works fine (as it should). What version of Linux or the shell are you running?