blob: 4f8d5bdea4598b385a39df4a156bd38bbf6a8494 [file] [log] [blame]
/***************************************************************************
*
* memchk.cpp - definitions of memory checking helper functions
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright 2001-2006 Rogue Wave Software.
*
**************************************************************************/
#ifndef _MSC_VER
# include <fcntl.h> // for open()
# include <unistd.h> // for getpagesize(), write()
#else // if MSVC
# include <fcntl.h> // for POSIX compatibility APIs
# include <io.h> // ditto
# include <windows.h> // for all of Win32 junk
#endif // MSVC
#include <errno.h> // for errno, EINTR
#include <stddef.h> // for size_t
#include <stdio.h> // for L_tmpnam, P_tmpdir, tempnam()
#include <stdlib.h> // for mkstemp()
#include <string.h> // for memchr()
#include <rw/_defs.h>
#ifndef EINTR
# define EINTR 4 /* AIX, HP-UX, IRIX, Linux, SunOS, Tru64 */
#endif // EINTR
#if defined (_RWSTD_NO_MKSTEMP) || !defined (_RWSTD_NO_PURE_C_HEADERS)
# ifndef _RWSTD_NO_MKSTEMP_IN_LIBC
extern "C" int mkstemp (char*) _LIBC_THROWS();
# undef _RWSTD_NO_MKSTEMP
# endif // _RWSTD_NO_MKSTEMP_IN_LIBC
#endif // _RWSTD_NO_MKSTEMP || !_RWSTD_NO_PURE_C_HEADERS
#ifndef P_tmpdir
# ifndef _P_tmpdir
# define P_tmpdir "/tmp/"
# else
# define P_tmpdir _P_tmpdir
# endif
#endif // P_tmpdir
static int page_size ()
{
static int size = 0;
if (0 == size) {
#if defined (_WIN32) || defined (_WIN64)
SYSTEM_INFO info;
GetSystemInfo (&info);
size = int (info.dwPageSize);
#else // any saner OS
size = getpagesize ();
#endif // WIN{32,64}
}
return size;
}
size_t memchk (const void *addr, size_t nbytes)
{
static int fd = -1;
if (-1 == fd) {
// writing to /dev/null need not reliably detect invalid
// address ranges if the operating system optimizes the
// operation away (as SunOS does, for instance)
// fd = open ("/dev/null", O_WRONLY);
#if defined (_WIN32) || defined (_WIN64)
char* const fname = tempnam (P_tmpdir, ".rwmemchk.tmp");
if (!fname)
return size_t (-1);
// create a temporary file and have Win32 delete it when
// the last file descriptor that refers to it is closed
fd = open (fname, O_RDWR | O_CREAT | _O_TEMPORARY, 0666);
// free storage allocated by tempnam()
free (fname);
if (fd < 0) {
// error: unable to check addr
return size_t (-1);
}
#else // !_WIN{32,64}
# define TMP_TEMPLATE P_tmpdir "/rwmemchk-XXXXXX"
char fname_buf [] = TMP_TEMPLATE;
fd = mkstemp (fname_buf);
if (fd < 0) {
// error: unable to check addr
return size_t (-1);
}
unlink (fname_buf);
#endif // _WIN{32,64}
}
lseek (fd, 0, SEEK_SET);
size_t size = size_t (-1); // error
const int errno_save = errno;
errno = 0;
if (size_t (-1) == nbytes) {
// size not specified, check that addr points
// to a valid NUL-terminated byte string (NTBS)
// get the virtual page size
static const size_t pgsz = page_size ();
// compute the positive offset from the closest page boundary
const size_t pgoff = size_t (addr) % pgsz;
// compute the number of bytes from addr to the end of the page
nbytes = pgsz - pgoff;
// go through memory pointed to by `addr' one page
// at a time looking for the terminating NUL
for (const char *pc = static_cast<const char*>(addr); ; ) {
// try to write a page of memory (or what's left of it)
const size_t nwrote = write (fd, pc, nbytes);
if (nwrote == nbytes) {
// on success, look for the NUL character
const void* const pnull = memchr (pc, 0, nbytes);
if (pnull) {
// return the offset of the NUL character from `addr'
size = static_cast<const char*>(pnull)
- static_cast<const char*>(addr) + 1;
break;
}
pc += nbytes;
nbytes = pgsz;
}
else if (size_t (-1) == nwrote && EINTR == errno) {
// retry the interrupted system call
errno = 0;
}
else if (nwrote < nbytes) {
// retry after a partial write
errno = 0;
pc += nwrote;
nbytes -= nwrote;
}
else {
// error: `addr' doesn't point to a valid NTBS
break;
}
}
}
else {
const size_t nwrote = write (fd, addr, nbytes);
if (nwrote == nbytes)
size = nbytes;
}
errno = errno_save;
return size_t (size);
}
size_t strchk (const char *str)
{
return memchk (str, size_t (-1));
}