Re: don't let mmap allocate down to zero

From: linux-os
Date: Wed Jan 26 2005 - 21:33:21 EST


On Wed, 26 Jan 2005, Bryn Reeves wrote:

On Wed, 2005-01-26 at 17:34, Chris Friesen wrote:
linux-os wrote:

Does this mean that we can't mmap the screen regen buffer at
0x000b8000 anymore?

How do I look at the real-mode interrupt table starting at
offset 0? You know that the return value of mmap is to be
checked for MAP_FAILED, not for NULL, don't you?

Can't you still map those physical addresses to other virtual addresses?


I think that's the case. The 0 address as refered to here is only for
the user virtual address space.

What 'C' standard do you refer to? Seg-faults on null pointers
have nothing to do with the 'C' standard and everything to
do with the platform.

I believe the ISO/IEC 9899:1999 C Standard explicitly states that
dereferencing a null pointer with the unary * operator results in
undefined behavior.

Exactly. Undefined. VAX/UNIX allowed assignment to null pointers. BUT
it's now such a commonly held assumption that a null pointer is not
valid that things will break if this is changed. Doesn't glibc malloc
use mmap for small allocations? From the man page:

Wrong! A returned value of 0 is perfectly correct for mmap()
when mapping a fixed address. The attached code shows it working
perfectly while returning NULL, that event being put on the
terminal.

Any kernel changes that break this code are wrong. There are
many 'C' runtime library functions that signal a problem (or
should signal a problem) by returning NULL. That is, however,
the documented behavior of those procedures. Even malloc()
which __should__ show a failure to allocate by returning NULL,
doesn't work that way because the allocation never fails
(even if you are out of memory) as long as the user address
space hasn't been corrupted by the user (overwriting buffers).

The seg-fault you get when you de-reference a pointer to NULL
is caused by the kernel. You are attempting to access memory
that has not been mapped into your address space. Once that
memory gets mmap()ed, you will no longer get a seg-fault.
Again, the seg-fault has nothing to do with 'C'. It's an
implementation behavior that can be changed with mmap().

[SNIPPED...]

Cheers,
Dick Johnson
Penguin : Linux version 2.6.10 on an i686 machine (5537.79 BogoMips).
Notice : All mail here is now cached for review by Dictator Bush.
98.36% of all statistics are fiction.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>

#define xxDEBUG

#if !defined(PAGE_SIZE)
#define PAGE_SIZE 0x1000
#endif
#if !defined(PAGE_MASK)
#define PAGE_MASK ~(PAGE_SIZE - 1)
#endif
#if !defined(MAP_FAILED)
#define MAP_FAILED (void *) -1
#endif
#define UL unsigned long

#define ERRORS \
{ fprintf(stderr, errmsg, __LINE__,\
__FILE__, errno, strerror(errno));\
exit(EXIT_FAILURE); }

#ifdef DEBUG
#define DEB(f) (f)
#else
#define DEB(f)
#endif

static const char errmsg[]="Error at line %d, file %s (%d) [%s]\n";
static const char mapfile[]="/dev/mem";
#define PROT (PROT_READ|PROT_WRITE)
#define FLAGS (MAP_FIXED|MAP_SHARED)
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*
* This writes a 'debug-like' dump out the screen. It always writes
* 16 lines of 16 characters. It returns the last address displayed.
*/
static off_t dump(off_t addr);

static off_t dump(off_t addr)
{
size_t i, j;
int fd;
caddr_t mem;
off_t next;
unsigned char c, *buf;

next = addr;
addr &= PAGE_MASK;
if((fd = open(mapfile, O_RDWR)) < 0)
ERRORS;

if((mem = mmap((caddr_t)addr, PAGE_SIZE * 2, PROT, FLAGS, fd, addr)) == MAP_FAILED)
{
(void)close(fd);
fprintf(stderr, "\tCan't map address %08lX\n", (UL) addr);
return (off_t)mem;
}


printf("mmap returned %p (0x%08x)\n", mem, (size_t) mem);

buf = (unsigned char *) next;
DEB(fprintf(stderr, "Mapped address = 0x%lx\n", addr));
DEB(fprintf(stderr, "Buffer address = %p\n", buf));

for(i=0; i< 0x10; i++)
{
fprintf(stdout, "\n%08lX ", (UL) next);
next += 0x10;
for(j=0; j< 0x10; j++)
fprintf(stdout, "%02X ", (unsigned int) *buf++);
buf -= 0x10;
for(j=0; j<0x10; j++)
{
c = *buf++;
if((c < (unsigned char) ' ') || (c > (unsigned char) 'z'))
c = (unsigned char) '.';
fprintf(stdout, "%c", c);
}
}
fprintf(stdout, "\n");
(void)fflush(stdout);
(void)close(fd);
if(munmap(mem, PAGE_SIZE)< 0)
ERRORS;
return next;
}

void usage(char *cp)
{
fprintf(stderr, "Usage\n%s <start address> [end address]\n", cp);
exit(EXIT_FAILURE);
}

int main(int args, char *argv[])
{
off_t addr;
off_t end;

if(args < 2)
usage(argv[0]);
end = 0;
if(sscanf(argv[1], "%lx", &addr) != 1)
usage(argv[0]);
if(argv[2] != NULL)
(void)sscanf(argv[2], "%lx", &end);
do {
if((addr = dump(addr)) == 0xffffffff)
break;
} while (end > addr);
return 0;
}