mprotect and file handles - c++

I have this simple program where I am trying to protect a block of memory, and then read a file into that memory, releasing it when it segfaults..
first I thought there was only a problem if the file is a fifo.. but now it seems that even for a normal file it fails,
this is the code:
#include <errno.h>
#include <string.h>
#include <iostream>
#include <assert.h>
#include <malloc.h>
#include <sys/mman.h>
#include <unistd.h>
#include <map>
#include <algorithm>
#include <unistd.h>
#include <signal.h>
using namespace std;
#define BUFFER_SIZE 8000
#define handle_error(msg) \
do { cout << __LINE__ << endl ;perror(msg); exit(EXIT_FAILURE); } while (0)
volatile int fault_count = 0;
char* buffer = 0;
int size = 40960;
int my_fault_handler(void* addr, int serious) {
if (mprotect(buffer, size,
PROT_READ | PROT_WRITE) == -1)
handle_error("mprotect");
++fault_count;
cout << "Segfaulting" << endl;
return 1;
}
static void handler(int sig, siginfo_t *si, void *unused) {
my_fault_handler(si ->si_addr, sig);
}
int main (int argc, char *argv[])
{
long pagesize = sysconf(_SC_PAGESIZE);
struct sigaction sa;
sa.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = &handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
perror("sigaction");
cerr << "pageSize: " << pagesize << endl;
buffer = (char*)memalign(pagesize, size);
if (buffer == NULL)
handle_error("memalign");
if (mprotect(buffer, size, PROT_READ) == -1)
handle_error("mprotect");
FILE* file = fopen("test", "r");
cout << "File Open" << endl;
if (!file) {
cout << "Failed opening file " << strerror(errno) << endl;
return 0;
}
//*buffer = 0;
while(fread(buffer, pagesize*2, 1, file)) {
if (mprotect(buffer, size,
PROT_READ) == -1)
handle_error("mprotect");
}
cout << ' ' << strerror(errno) << endl;
return(0);
}
note the //*buffer = 0;, if I unmark this line the program segfaults and works correctly..
anyone has any idea?
the errno is bad address.
Thanks!
UPDATE:
It seems a similiar question was asked here:
Loading MachineCode From File Into Memory and Executing in C -- mprotect Failing
where posix_memalign was suggested, I have tried this and it didn't work.

The problem is that you're not checking for an error in the FILE handle after a short read.
What the system would tell you is that the first fread failed and didn't trigger the fault handler.
If you checked for ferror outside the loop (sloppy as an example):
while(fread(buffer, pagesize*2, 1, file)) {
if (mprotect(buffer, size,
PROT_READ) == -1)
handle_error("mprotect");
}
if (ferror(file) != 0) {
cout << "Error" << endl;
}
Why it failed is that the underlying read failed, and returned an errno of 14 (EFAULT), which is not quite what is documented to happen when read fails in this situation (it says that Buf points outside the allocated address space.)
You can only trust the signal handler to be triggered in the mprotect case when the code in question is running in the user context, most system calls will fail and return EFAULT in the case that the buffer is invalid or does not have the correct permissions.

Related

What should the error statements be for exit 3 and 4 in my c++ simple buffer code?

I need to input error statements above exits three and four and then actually force the code to go to these error statements so I can have screenshot proof that they are working. However, I can't quite work out what should be in each. My initial thoughts are 'the output file can't be created for 3 and 'The file you want to read from is empty' for 4, but I can't seem to trigger these errors so I feel like that's not correct.
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 500
#define OUTPUT_MODE 0700
int main(int argc, char *argv[])
{
int in_file, out_file;
int read_size = 1, write_size;
char buf[BUF_SIZE];
if (argc != 3){
cout<<"The command-line input does not contain 3 arguments"<<endl;
exit(1);
}
in_file= open(argv[1], O_RDONLY);
if (in_file < 0) {
cout<<"The file you are trying to copy from doesnt exist"<<endl;
exit(2);
}
out_file = creat(argv[2], OUTPUT_MODE);
if (out_file < 0) {
cout<<"Error statement 3"<<endl;
exit(3);
}
while (read_size > 0) {
read_size = read(in_file, buf, BUF_SIZE);
if (read_size <0){
cout<<"Error statement 4"<<endl;
exit(4);
}
write_size = write(out_file, buf, read_size);
if (write_size<=0){
close(in_file);
close(out_file);
cout<<"Reading and writing from and to files is complete"<<endl;
exit(5);
}
}
}
The way to work out how these functions can /will fail, is to read the documentation for them.
https://man7.org/linux/man-pages/man2/read.2.html
https://man7.org/linux/man-pages/man2/open.2.html
(note, the documentation of creat says its equivalent to calling open with specific arguments)
At the bottom, it lists what errno will be set to and why.
Open, for example, will fail on a read only disk.
Most standard library functions will set errno to give you the reason they failed. Use that information, and write your error messages to stderr:
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#define OUTPUT_MODE 0700
int
main(int argc, char *argv[])
{
int in_file, out_file;
int read_size, write_size;
char buf[BUFSIZ];
if (argc != 3){
cerr << "Missing arguments" <<endl;
exit(EXIT_FAILURE);
}
in_file= open(argv[1], O_RDONLY);
if( in_file < 0 ){
cerr << argv[1] << ": " << strerror(errno) << endl;
exit(EXIT_FAILURE);
}
out_file = creat(argv[2], OUTPUT_MODE);
if( out_file < 0 ){
cerr << argv[2] << ": " << strerror(errno) << endl;
exit(EXIT_FAILURE);
}
while( (read_size = read(in_file, buf, BUFSIZ)) > 0 ){
const char *s = buf;
do {
write_size = write(out_file, s, read_size);
read_size -= write_size;
if( write_size <= 0 ){
cerr << argv[2] << ": " << strerror(errno) << endl;
exit(EXIT_FAILURE);
}
s += write_size;
} while( read_size > 0);
}
if( read_size < 0 ){
cerr << argv[1] << ": " << strerror(errno) << endl;
exit(EXIT_FAILURE);
}
close(in_file);
close(out_file);
}

Setting ctrl with V4L2 on ov5640

I would like to control various ov5640 camera parameters by using ioctl and VIDIOC_S_CTRL from V4L2 in the following manner:
#include <string>
#include <iostream>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <cstring>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define IOCTL_TRIES 3
#define CLEAR(x) memset (&(x), 0, sizeof (x))
static int xioctl(int fd, int request, void *arg)
{
int r;
int tries = IOCTL_TRIES;
do {
r = ioctl(fd, request, arg);
} while (--tries > 0 && r == -1 && EINTR == errno);
return r;
}
bool v4l2_ctrl_set(int fd, uint32_t id, int val)
{
struct v4l2_control ctrl;
CLEAR(ctrl);
ctrl.id = id;
ctrl.value = val;
if (xioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) {
std::cout << "Failed to set ctrl with id "
<< id << " to value " << val
<< "\nerror (" << errno << "): " << strerror(errno) << std::endl;
return false;
}
return true;
}
int main()
{
int fd = open("/dev/video0", O_RDWR | O_NONBLOCK);
if (fd == -1) {
std::cout << "Failed to open the camera" << std::endl;
return -1;
}
v4l2_ctrl_set(fd, V4L2_CID_SATURATION, 100);
return 0;
}
Unfortunately ioctl fails and I get error (25): Inappropriate ioctl for device. I'm using Intrinsyc Open-Q 820 µSOM with linaro 4.14. I've managed to add some debugs prints to ov5640 driver file in ov5640_s_ctrl function before if (sensor->power_count == 0) { (in case there were problems with power save mode) and recompile the kernel. I ran the code, but looking through dmesg my printk message doesn't get printed, so that means that ov5640_s_ctrl doesn't get called even though the callback is set:
static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
.g_volatile_ctrl = ov5640_g_volatile_ctrl,
.s_ctrl = ov5640_s_ctrl,
};
Am I using V4L2 wrong? Should I enable something before setting the controls? It's even more confusing since I manage to get an image from the camera with v4l2, but I can't set/get any controls.
In the the kernel source code of ov5640.c that you supplied, the driver is assigned the flag V4L2_SUBDEV_FL_HAS_DEVNODE which means it might supply a subdev node /dev/v4l-subdevX. According to the kernel docs:
Device nodes named v4l-subdevX can be created in /dev to access sub-devices directly. If a sub-device supports direct userspace configuration it must set the V4L2_SUBDEV_FL_HAS_DEVNODE flag before being registered.`
So you can try to set the control directly from the v4l-subdevX node if it exists.

C++ mincore return vector every byte is 1

I use mincore to judge memory by mmap open in memory or disk. but return a set vector. Why? In fact the result must be a all clear vector, but I get all set.
This is my code. Why is line 28 (cout << "find" << endl;) always skipped?
/proc/pid/smap can see RSS is 0, but mincore return total file in memory.
#include <iostream>
#include <unistd.h>
#include <sys/mman.h>
#include <bitset>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
using namespace std;
int main()
{
char* pData1 = NULL;
int fd1 = open("test_large_file_1", O_RDWR);
if (fd1 == -1)
{
cout << "file error ..." << endl;
return -1;
}
off_t size1 = lseek(fd1, 0, SEEK_END);
if (size1 == -1)
{
cout << "lseek error ..." << endl;
return -1;
}
pData1 = (char *)mmap(NULL, size1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd1, 0 );
if (pData1 == MAP_FAILED)
{
cout << "mmap error ..." << endl;
return -1;
}
unsigned char *pVec = new unsigned char[size1 / 1024 / 4];
if (-1 == mincore(pData1, size1, pVec))
{
cout << "mincore error ..." << endl;
return -1;
}
for (int i = 0; i < size1 / 1024/ 4; ++i)
{
if (i % 1000 == 0)
cout << (int)pVec[i] << endl;
if ((pVec[i] & 1) == 0)
{
cout << "find" << endl;
break;
}
}
close(fd1);
munmap((void *)pData1, size1);
return 0;
}
I want to get whether an address by mmap opening in memory or not, veteran has some way?/
I need help.
I get a old file(don't open long time),mincore return a normal vector(has 0 and 1), but a new file(just now open or read ...),mincore return a all set bit vector.
This phenomenon is due to page cache, that will save recently page to cache, if a program repeatly open a file, the page of file will be get in memory .

Size is always zero when reading DRM EDID file

I tried to read the EDID of my monitor connected to LVDS1. I use ArchLinux and C++/clang. My problem is: the file size always returns 0. I don't know if this is a programming problem or something OS specific, other files return a proper file size. Is it a special file? Is the symlink directory /sys/class/drm/card0-DP-1 the problem?
file: /sys/class/drm/card0-LVDS-1/edid
code:
#include <fstream>
#include <iostream>
using namespace std;
typedef unsigned char BYTE;
long
get_file_size(FILE *f)
{
long pos_cursor, pos_end;
pos_cursor = ftell(f);
fseek(f, 0, 2);
pos_end = ftell(f);
fseek(f, pos_cursor, 0);
return pos_end;
}
int
main()
{
const char *filepath = "/sys/class/drm/card0-LVDS-1/edid";
FILE *file = NULL;
if((file = fopen(filepath, "rb")) == NULL)
{
cout << "file could not be opened" << endl;
return 1;
}
else
cout << "file opened" << endl;
long filesize = get_file_size(file);
cout << "file size: " << filesize << endl;
fclose(file);
return 0;
}
output:
file opened
file size: 0
===
as suggested by MSalters, I tried stat for the file size. Also returns 0. I assume the code is correct, so it is somehow just not possible to access the file?
I also tried the symlink target path (/sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-LVDS-1/edid), just in case that was the problem - but still 0.
code:
#include <iostream>
#include <sys/stat.h>
using namespace std;
int
main()
{
const char *filepath = "/sys/class/drm/card0-LVDS-1/edid";
struct stat results;
if (stat(filepath, &results) == 0)
cout << results.st_size << endl;
else
cout << "error" << endl;
return 0;
}
output
0
===
I tried other files in the same directory (dpms edid enabled i2c-6 modes power status subsystem uevent). They all return a filesize of 4096 except edid.
I suspect that fseek(f, 0, 2); might mean fseek(f, 0, SEEK_CUR); which obviously doesn't do anything. You'd want SEEK_END which isn't portable, but then again /sys/ isn't either. (Of course, do #include <stdio.h>)
But considering it's already Linux-specific, why not use stat ?

posix_spawnp and piping child output to a string

I am struggling with process creation and piping the child process' output into a string of the parent process. I got it working on Windows (using CreatePipe and CreateProcess and ReadFile), but can't seem to get the exact analog on Unix to work. This is my code:
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;
if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);
posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);
vector<string> argmem = {"bla"};
vector<char*> args = {&argmem[0][0], nullptr}; // I don't want to call new.
pid_t pid;
if(posix_spawnp(&pid, "echo", &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";
//close(cout_pipe[0]);
//close(cerr_pipe[0]);
close(cout_pipe[1]);
close(cerr_pipe[1]);
waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";
// Read from pipes
const size_t buffer_size = 1024;
string buffer;
buffer.resize(buffer_size);
ssize_t bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
while ((bytes_read = read(cout_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stdout pipe.\n";
while ((bytes_read = read(cerr_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stderr pipe.\n";
posix_spawn_file_actions_destroy(&action);
}
The output is:
exit code: 0
So I suppose everything is working except the actual piping. What is wrong here? I also wonder if there is a way to read the piped bytes in a waitpid loop, but when I try that, the parent process hangs infinitely.
posix_spawn is interesting and useful, which makes this question worth necromancing -- even if it is no longer relevant to the OP.
There are some significant bugs in the code as posted. I suspect that some of these were the result of hacking in desperation, but I don't know which was the original bug:
The args array does not include the argv[0] that would represent the executable name. This results in the echo program never seeing the intended argv[1] ("bla").
The read() function is called from different places in a way that just doesn't make sense. A correct way to do this would be to only call read as part of the control expression for the while loops.
waitpid() is called before reading from the pipes. This prevents the I/O from completing (in non-trivial cases at least).
A more subtle issue with this code is that attempts to read all of the child's stdout before reading anything from stderr. In principle, this could cause the child to block while attempting to write to stderr, thus preventing the program from completing. Creating an efficient solution to this is more complicated as it requires that you can read from whichever pipe has available data. I used poll() for this. Another approach would be to use multiple threads.
Additionally, I have used sh (the command shell, i.e. bash) as the child process. This provides a great deal of additional flexibility, such as running a pipeline instead of a single executable. In particular, though, using sh provides the simple convenience of not having to manage the parsing of the command-line.
/*BINFMTCXX: -std=c++11 -Wall -Werror
*/
#include <spawn.h> // see manpages-posix-dev
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;
if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);
posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);
//string command = "echo bla"; // example #1
string command = "pgmcrater -width 64 -height 9 |pgmtopbm |pnmtoplainpnm";
string argsmem[] = {"sh","-c"}; // allows non-const access to literals
char * args[] = {&argsmem[0][0],&argsmem[1][0],&command[0],nullptr};
pid_t pid;
if(posix_spawnp(&pid, args[0], &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";
close(cout_pipe[1]), close(cerr_pipe[1]); // close child-side of pipes
// Read from pipes
string buffer(1024,' ');
std::vector<pollfd> plist = { {cout_pipe[0],POLLIN}, {cerr_pipe[0],POLLIN} };
for ( int rval; (rval=poll(&plist[0],plist.size(),/*timeout*/-1))>0; ) {
if ( plist[0].revents&POLLIN) {
int bytes_read = read(cout_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else if ( plist[1].revents&POLLIN ) {
int bytes_read = read(cerr_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else break; // nothing left to read
}
waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";
posix_spawn_file_actions_destroy(&action);
}