Made Addrcheck distinguish between invalid reads and invalid writes (previously
was just saying "invalid memory access").
Added a regression test for this, for memcheck and addrcheck. Also made
Addrcheck use Memcheck's fprw regtest. Was able to remove the not-very-useful
'true' test for Addrcheck now that it has a couple of real tests.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1815 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/addrcheck/ac_main.c b/addrcheck/ac_main.c
index 2efaa3a..760e219 100644
--- a/addrcheck/ac_main.c
+++ b/addrcheck/ac_main.c
@@ -55,9 +55,11 @@
case AddrErr:
switch (err_extra->axskind) {
case ReadAxs:
+ VG_(message)(Vg_UserMsg, "Invalid read of size %d",
+ err_extra->size );
+ break;
case WriteAxs:
- /* These two aren't actually differentiated ever. */
- VG_(message)(Vg_UserMsg, "Invalid memory access of size %d",
+ VG_(message)(Vg_UserMsg, "Invalid write of size %d",
err_extra->size );
break;
case ExecAxs:
@@ -156,10 +158,10 @@
/*--- Function declarations. ---*/
/*------------------------------------------------------------*/
-static void ac_ACCESS4_SLOWLY ( Addr a );
-static void ac_ACCESS2_SLOWLY ( Addr a );
-static void ac_ACCESS1_SLOWLY ( Addr a );
-static void ac_fpu_ACCESS_check_SLOWLY ( Addr addr, Int size );
+static void ac_ACCESS4_SLOWLY ( Addr a, Bool isWrite );
+static void ac_ACCESS2_SLOWLY ( Addr a, Bool isWrite );
+static void ac_ACCESS1_SLOWLY ( Addr a, Bool isWrite );
+static void ac_fpu_ACCESS_check_SLOWLY ( Addr addr, Int size, Bool isWrite );
/*------------------------------------------------------------*/
/*--- Data defns. ---*/
@@ -682,11 +684,10 @@
Under all other circumstances, it defers to the relevant _SLOWLY
function, which can handle all situations.
*/
-__attribute__ ((regparm(1)))
-static void ac_helperc_ACCESS4 ( Addr a )
+static __inline__ void ac_helperc_ACCESS4 ( Addr a, Bool isWrite )
{
# ifdef VG_DEBUG_MEMORY
- return ac_ACCESS4_SLOWLY(a);
+ return ac_ACCESS4_SLOWLY(a, isWrite);
# else
UInt sec_no = rotateRight16(a) & 0x3FFFF;
AcSecMap* sm = primary_map[sec_no];
@@ -701,16 +702,15 @@
return;
} else {
/* Slow but general case. */
- ac_ACCESS4_SLOWLY(a);
+ ac_ACCESS4_SLOWLY(a, isWrite);
}
# endif
}
-__attribute__ ((regparm(1)))
-static void ac_helperc_ACCESS2 ( Addr a )
+static __inline__ void ac_helperc_ACCESS2 ( Addr a, Bool isWrite )
{
# ifdef VG_DEBUG_MEMORY
- return ac_ACCESS2_SLOWLY(a);
+ return ac_ACCESS2_SLOWLY(a, isWrite);
# else
UInt sec_no = rotateRight16(a) & 0x1FFFF;
AcSecMap* sm = primary_map[sec_no];
@@ -721,16 +721,15 @@
return;
} else {
/* Slow but general case. */
- ac_ACCESS2_SLOWLY(a);
+ ac_ACCESS2_SLOWLY(a, isWrite);
}
# endif
}
-__attribute__ ((regparm(1)))
-static void ac_helperc_ACCESS1 ( Addr a )
+static __inline__ void ac_helperc_ACCESS1 ( Addr a, Bool isWrite )
{
# ifdef VG_DEBUG_MEMORY
- return ac_ACCESS1_SLOWLY(a);
+ return ac_ACCESS1_SLOWLY(a, isWrite);
# else
UInt sec_no = shiftRight16(a);
AcSecMap* sm = primary_map[sec_no];
@@ -741,18 +740,51 @@
return;
} else {
/* Slow but general case. */
- ac_ACCESS1_SLOWLY(a);
+ ac_ACCESS1_SLOWLY(a, isWrite);
}
# endif
}
+__attribute__ ((regparm(1)))
+static void ac_helperc_LOAD4 ( Addr a )
+{
+ ac_helperc_ACCESS4 ( a, /*isWrite*/False );
+}
+__attribute__ ((regparm(1)))
+static void ac_helperc_STORE4 ( Addr a )
+{
+ ac_helperc_ACCESS4 ( a, /*isWrite*/True );
+}
+
+__attribute__ ((regparm(1)))
+static void ac_helperc_LOAD2 ( Addr a )
+{
+ ac_helperc_ACCESS2 ( a, /*isWrite*/False );
+}
+__attribute__ ((regparm(1)))
+static void ac_helperc_STORE2 ( Addr a )
+{
+ ac_helperc_ACCESS2 ( a, /*isWrite*/True );
+}
+
+__attribute__ ((regparm(1)))
+static void ac_helperc_LOAD1 ( Addr a )
+{
+ ac_helperc_ACCESS1 ( a, /*isWrite*/False );
+}
+__attribute__ ((regparm(1)))
+static void ac_helperc_STORE1 ( Addr a )
+{
+ ac_helperc_ACCESS1 ( a, /*isWrite*/True );
+}
+
/*------------------------------------------------------------*/
/*--- Fallback functions to handle cases that the above ---*/
-/*--- VG_(helperc_ACCESS{1,2,4}) can't manage. ---*/
+/*--- ac_helperc_ACCESS{1,2,4} can't manage. ---*/
/*------------------------------------------------------------*/
-static void ac_ACCESS4_SLOWLY ( Addr a )
+static void ac_ACCESS4_SLOWLY ( Addr a, Bool isWrite )
{
Bool a0ok, a1ok, a2ok, a3ok;
@@ -781,7 +813,7 @@
if (!MAC_(clo_partial_loads_ok)
|| ((a & 3) != 0)
|| (!a0ok && !a1ok && !a2ok && !a3ok)) {
- MAC_(record_address_error)( VG_(get_current_tid)(), a, 4, False );
+ MAC_(record_address_error)( VG_(get_current_tid)(), a, 4, isWrite );
return;
}
@@ -797,7 +829,7 @@
}
}
-static void ac_ACCESS2_SLOWLY ( Addr a )
+static void ac_ACCESS2_SLOWLY ( Addr a, Bool isWrite )
{
/* Check the address for validity. */
Bool aerr = False;
@@ -808,11 +840,11 @@
/* If an address error has happened, report it. */
if (aerr) {
- MAC_(record_address_error)( VG_(get_current_tid)(), a, 2, False );
+ MAC_(record_address_error)( VG_(get_current_tid)(), a, 2, isWrite );
}
}
-static void ac_ACCESS1_SLOWLY ( Addr a )
+static void ac_ACCESS1_SLOWLY ( Addr a, Bool isWrite)
{
/* Check the address for validity. */
Bool aerr = False;
@@ -822,7 +854,7 @@
/* If an address error has happened, report it. */
if (aerr) {
- MAC_(record_address_error)( VG_(get_current_tid)(), a, 1, False );
+ MAC_(record_address_error)( VG_(get_current_tid)(), a, 1, isWrite );
}
}
@@ -831,8 +863,8 @@
FPU load and store checks, called from generated code.
------------------------------------------------------------------ */
-__attribute__ ((regparm(2)))
-static void ac_fpu_ACCESS_check ( Addr addr, Int size )
+static __inline__
+void ac_fpu_ACCESS_check ( Addr addr, Int size, Bool isWrite )
{
/* Ensure the read area is both addressible and valid (ie,
readable). If there's an address error, don't report a value
@@ -849,7 +881,7 @@
PROF_EVENT(90);
# ifdef VG_DEBUG_MEMORY
- ac_fpu_ACCESS_check_SLOWLY ( addr, size );
+ ac_fpu_ACCESS_check_SLOWLY ( addr, size, isWrite );
# else
if (size == 4) {
@@ -863,7 +895,7 @@
/* Properly aligned and addressible. */
return;
slow4:
- ac_fpu_ACCESS_check_SLOWLY ( addr, 4 );
+ ac_fpu_ACCESS_check_SLOWLY ( addr, 4, isWrite );
return;
}
@@ -887,7 +919,7 @@
/* Both halves properly aligned and addressible. */
return;
slow8:
- ac_fpu_ACCESS_check_SLOWLY ( addr, 8 );
+ ac_fpu_ACCESS_check_SLOWLY ( addr, 8, isWrite );
return;
}
@@ -895,13 +927,13 @@
cases go quickly. */
if (size == 2) {
PROF_EVENT(93);
- ac_fpu_ACCESS_check_SLOWLY ( addr, 2 );
+ ac_fpu_ACCESS_check_SLOWLY ( addr, 2, isWrite );
return;
}
if (size == 16 || size == 10 || size == 28 || size == 108) {
PROF_EVENT(94);
- ac_fpu_ACCESS_check_SLOWLY ( addr, size );
+ ac_fpu_ACCESS_check_SLOWLY ( addr, size, isWrite );
return;
}
@@ -910,12 +942,23 @@
# endif
}
+__attribute__ ((regparm(2)))
+static void ac_fpu_READ_check ( Addr addr, Int size )
+{
+ ac_fpu_ACCESS_check ( addr, size, /*isWrite*/False );
+}
+
+__attribute__ ((regparm(2)))
+static void ac_fpu_WRITE_check ( Addr addr, Int size )
+{
+ ac_fpu_ACCESS_check ( addr, size, /*isWrite*/True );
+}
/* ---------------------------------------------------------------------
Slow, general cases for FPU access checks.
------------------------------------------------------------------ */
-void ac_fpu_ACCESS_check_SLOWLY ( Addr addr, Int size )
+void ac_fpu_ACCESS_check_SLOWLY ( Addr addr, Int size, Bool isWrite )
{
Int i;
Bool aerr = False;
@@ -927,7 +970,7 @@
}
if (aerr) {
- MAC_(record_address_error)( VG_(get_current_tid)(), addr, size, False );
+ MAC_(record_address_error)( VG_(get_current_tid)(), addr, size, isWrite );
}
}
@@ -945,6 +988,7 @@
Int i;
UInstr* u_in;
Int t_addr, t_size;
+ Addr helper;
cb = VG_(setup_UCodeBlock)(cb_in);
@@ -960,28 +1004,33 @@
/* For memory-ref instrs, copy the data_addr into a temporary to be
* passed to the helper at the end of the instruction.
*/
- case LOAD:
- t_addr = u_in->val1;
- goto do_LOAD_or_STORE;
- case STORE: t_addr = u_in->val2;
- goto do_LOAD_or_STORE;
- do_LOAD_or_STORE:
- uInstr1(cb, CCALL, 0, TempReg, t_addr);
+ case LOAD:
switch (u_in->size) {
- case 4: uCCall(cb, (Addr) & ac_helperc_ACCESS4, 1, 1, False );
- break;
- case 2: uCCall(cb, (Addr) & ac_helperc_ACCESS2, 1, 1, False );
- break;
- case 1: uCCall(cb, (Addr) & ac_helperc_ACCESS1, 1, 1, False );
- break;
- default:
- VG_(skin_panic)("addrcheck::SK_(instrument):LOAD/STORE");
+ case 4: helper = (Addr)ac_helperc_LOAD4; break;
+ case 2: helper = (Addr)ac_helperc_LOAD2; break;
+ case 1: helper = (Addr)ac_helperc_LOAD1; break;
+ default: VG_(skin_panic)("addrcheck::SK_(instrument):LOAD");
}
+ uInstr1(cb, CCALL, 0, TempReg, u_in->val1);
+ uCCall (cb, helper, 1, 1, False );
+ VG_(copy_UInstr)(cb, u_in);
+ break;
+
+ case STORE:
+ switch (u_in->size) {
+ case 4: helper = (Addr)ac_helperc_STORE4; break;
+ case 2: helper = (Addr)ac_helperc_STORE2; break;
+ case 1: helper = (Addr)ac_helperc_STORE1; break;
+ default: VG_(skin_panic)("addrcheck::SK_(instrument):STORE");
+ }
+ uInstr1(cb, CCALL, 0, TempReg, u_in->val2);
+ uCCall (cb, helper, 1, 1, False );
VG_(copy_UInstr)(cb, u_in);
break;
case SSE3ag_MemRd_RegWr:
sk_assert(u_in->size == 4 || u_in->size == 8);
+ helper = (Addr)ac_fpu_READ_check;
goto do_Access_ARG1;
do_Access_ARG1:
sk_assert(u_in->tag1 == TempReg);
@@ -990,16 +1039,23 @@
uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size);
uLiteral(cb, u_in->size);
uInstr2(cb, CCALL, 0, TempReg, t_addr, TempReg, t_size);
- uCCall(cb, (Addr) & ac_fpu_ACCESS_check, 2, 2, False );
+ uCCall(cb, helper, 2, 2, False );
VG_(copy_UInstr)(cb, u_in);
break;
case MMX2_MemRd:
+ sk_assert(u_in->size == 4 || u_in->size == 8);
+ helper = (Addr)ac_fpu_READ_check;
+ goto do_Access_ARG2;
case MMX2_MemWr:
sk_assert(u_in->size == 4 || u_in->size == 8);
+ helper = (Addr)ac_fpu_WRITE_check;
goto do_Access_ARG2;
case FPU_R:
+ helper = (Addr)ac_fpu_READ_check;
+ goto do_Access_ARG2;
case FPU_W:
+ helper = (Addr)ac_fpu_WRITE_check;
goto do_Access_ARG2;
do_Access_ARG2:
sk_assert(u_in->tag2 == TempReg);
@@ -1008,25 +1064,27 @@
uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size);
uLiteral(cb, u_in->size);
uInstr2(cb, CCALL, 0, TempReg, t_addr, TempReg, t_size);
- uCCall(cb, (Addr) & ac_fpu_ACCESS_check, 2, 2, False );
+ uCCall(cb, helper, 2, 2, False );
VG_(copy_UInstr)(cb, u_in);
break;
case SSE3a_MemRd: // this one causes trouble
case SSE2a_MemRd:
+ helper = (Addr)ac_fpu_READ_check;
+ goto do_Access_ARG3;
case SSE2a_MemWr:
case SSE3a_MemWr:
- sk_assert(u_in->size == 4 || u_in->size == 8
- || u_in->size == 16);
+ helper = (Addr)ac_fpu_WRITE_check;
goto do_Access_ARG3;
do_Access_ARG3:
+ sk_assert(u_in->size == 4 || u_in->size == 8 || u_in->size == 16);
sk_assert(u_in->tag3 == TempReg);
t_addr = u_in->val3;
t_size = newTemp(cb);
uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size);
uLiteral(cb, u_in->size);
uInstr2(cb, CCALL, 0, TempReg, t_addr, TempReg, t_size);
- uCCall(cb, (Addr) & ac_fpu_ACCESS_check, 2, 2, False );
+ uCCall(cb, helper, 2, 2, False );
VG_(copy_UInstr)(cb, u_in);
break;
@@ -1288,10 +1346,14 @@
VG_(track_pre_mem_write) ( & ac_check_is_writable );
VG_(track_post_mem_write) ( & ac_make_accessible );
- VG_(register_compact_helper)((Addr) & ac_helperc_ACCESS4);
- VG_(register_compact_helper)((Addr) & ac_helperc_ACCESS2);
- VG_(register_compact_helper)((Addr) & ac_helperc_ACCESS1);
- VG_(register_compact_helper)((Addr) & ac_fpu_ACCESS_check);
+ VG_(register_compact_helper)((Addr) & ac_helperc_LOAD4);
+ VG_(register_compact_helper)((Addr) & ac_helperc_LOAD2);
+ VG_(register_compact_helper)((Addr) & ac_helperc_LOAD1);
+ VG_(register_compact_helper)((Addr) & ac_helperc_STORE4);
+ VG_(register_compact_helper)((Addr) & ac_helperc_STORE2);
+ VG_(register_compact_helper)((Addr) & ac_helperc_STORE1);
+ VG_(register_noncompact_helper)((Addr) & ac_fpu_READ_check);
+ VG_(register_noncompact_helper)((Addr) & ac_fpu_WRITE_check);
VGP_(register_profile_event) ( VgpSetMem, "set-mem-perms" );
VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" );
diff --git a/addrcheck/tests/Makefile.am b/addrcheck/tests/Makefile.am
index e586af5..cf28101 100644
--- a/addrcheck/tests/Makefile.am
+++ b/addrcheck/tests/Makefile.am
@@ -1,4 +1,5 @@
noinst_SCRIPTS = filter_stderr
EXTRA_DIST = $(noinst_SCRIPTS) \
- true.stderr.exp true.vgtest
+ badrw.stderr.exp badrw.vgtest \
+ fprw.stderr.exp fprw.vgtest
diff --git a/addrcheck/tests/badrw.stderr.exp b/addrcheck/tests/badrw.stderr.exp
new file mode 100644
index 0000000..c231c5d
--- /dev/null
+++ b/addrcheck/tests/badrw.stderr.exp
@@ -0,0 +1,59 @@
+Invalid read of size 4
+ at 0x........: main (badrw.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 4
+ at 0x........: main (badrw.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid read of size 2
+ at 0x........: main (badrw.c:13)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 2
+ at 0x........: main (badrw.c:13)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid read of size 1
+ at 0x........: main (badrw.c:14)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 1 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 1
+ at 0x........: main (badrw.c:14)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 1 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
diff --git a/addrcheck/tests/badrw.vgtest b/addrcheck/tests/badrw.vgtest
new file mode 100644
index 0000000..6f68301
--- /dev/null
+++ b/addrcheck/tests/badrw.vgtest
@@ -0,0 +1,2 @@
+vgopts: -q
+prog: ../../memcheck/tests/badrw
diff --git a/addrcheck/tests/fprw.stderr.exp b/addrcheck/tests/fprw.stderr.exp
new file mode 100644
index 0000000..41f8ada
--- /dev/null
+++ b/addrcheck/tests/fprw.stderr.exp
@@ -0,0 +1,56 @@
+Invalid read of size 8
+ at 0x........: main (fprw.c:20)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 0 bytes inside a block of size 8 free'd
+ at 0x........: free (vg_replace_malloc.c:...)
+ by 0x........: main (fprw.c:18)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 8
+ at 0x........: main (fprw.c:20)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 0 bytes inside a block of size 8 free'd
+ at 0x........: free (vg_replace_malloc.c:...)
+ by 0x........: main (fprw.c:18)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid read of size 4
+ at 0x........: main (fprw.c:21)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 0 bytes inside a block of size 4 free'd
+ at 0x........: free (vg_replace_malloc.c:...)
+ by 0x........: main (fprw.c:19)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 4
+ at 0x........: main (fprw.c:21)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 0 bytes inside a block of size 4 free'd
+ at 0x........: free (vg_replace_malloc.c:...)
+ by 0x........: main (fprw.c:19)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_replace_malloc.c:...)
+ by 0x........: main (fprw.c:22)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is not stack'd, malloc'd or free'd
+
+Invalid write of size 8
+ at 0x........: main (fprw.c:24)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 0 bytes inside a block of size 4 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (fprw.c:23)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
diff --git a/addrcheck/tests/fprw.vgtest b/addrcheck/tests/fprw.vgtest
new file mode 100644
index 0000000..9db266e
--- /dev/null
+++ b/addrcheck/tests/fprw.vgtest
@@ -0,0 +1,2 @@
+vgopts: --single-step=yes -q
+prog: ../../memcheck/tests/fprw
diff --git a/addrcheck/tests/true.stderr.exp b/addrcheck/tests/true.stderr.exp
deleted file mode 100644
index e69de29..0000000
--- a/addrcheck/tests/true.stderr.exp
+++ /dev/null
diff --git a/addrcheck/tests/true.vgtest b/addrcheck/tests/true.vgtest
deleted file mode 100644
index 5c1535c..0000000
--- a/addrcheck/tests/true.vgtest
+++ /dev/null
@@ -1,2 +0,0 @@
-prog: ../../tests/true
-vgopts: -q
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index f2ccf5b..7573eda 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -14,6 +14,7 @@
badfree.stderr.exp badfree.vgtest \
badjump.stderr.exp badjump.vgtest \
badloop.stderr.exp badloop.vgtest \
+ badrw.stderr.exp badrw.vgtest \
brk.stderr.exp brk.vgtest \
buflen_check.stderr.exp buflen_check.vgtest \
clientperm.stderr.exp \
@@ -62,7 +63,7 @@
writev.stderr.exp writev.vgtest
check_PROGRAMS = \
- badaddrvalue badfree badjump badloop brk buflen_check \
+ badaddrvalue badfree badjump badloop badrw brk buflen_check \
clientperm custom_alloc \
doublefree error_counts errs1 exitprog fprw fwrite inits inline \
malloc1 malloc2 malloc3 manuel1 manuel2 manuel3 \
@@ -81,6 +82,7 @@
badfree_SOURCES = badfree.c
badjump_SOURCES = badjump.c
badloop_SOURCES = badloop.c
+badrw_SOURCES = badrw.c
brk_SOURCES = brk.c
buflen_check_SOURCES = buflen_check.c
clientperm_SOURCES = clientperm.c
diff --git a/memcheck/tests/badrw.c b/memcheck/tests/badrw.c
new file mode 100644
index 0000000..b72f393
--- /dev/null
+++ b/memcheck/tests/badrw.c
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+int main(void)
+{
+ void* x = malloc(10);
+
+ int* x4 = x-4;
+ short int* x2 = x-4;
+ char* x1 = x-1;
+
+ // Invalid reads and writes of sizes 4, 2, 1
+ int y4 = *x4; *x4 = y4;
+ short int y2 = *x2; *x2 = y2;
+ char y1 = *x1; *x1 = y1;
+
+ return 0;
+}
diff --git a/memcheck/tests/badrw.stderr.exp b/memcheck/tests/badrw.stderr.exp
new file mode 100644
index 0000000..c231c5d
--- /dev/null
+++ b/memcheck/tests/badrw.stderr.exp
@@ -0,0 +1,59 @@
+Invalid read of size 4
+ at 0x........: main (badrw.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 4
+ at 0x........: main (badrw.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid read of size 2
+ at 0x........: main (badrw.c:13)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 2
+ at 0x........: main (badrw.c:13)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 4 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid read of size 1
+ at 0x........: main (badrw.c:14)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 1 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Invalid write of size 1
+ at 0x........: main (badrw.c:14)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+ Address 0x........ is 1 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (badrw.c:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
diff --git a/memcheck/tests/badrw.vgtest b/memcheck/tests/badrw.vgtest
new file mode 100644
index 0000000..09c70a0
--- /dev/null
+++ b/memcheck/tests/badrw.vgtest
@@ -0,0 +1,2 @@
+prog: badrw
+vgopts: -q