FreeBSD Bugzilla – Attachment 143473 Details for
Bug 190735
truncate(1) integer overflow issues with size command line arg -- diff with unit tests attached
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
Diff that fixes bug. And adds new unit tests.
truncate_diff.txt (text/plain), 16.74 KB, created by
Kirk Russell
on 2014-06-06 23:56:49 UTC
(
hide
)
Description:
Diff that fixes bug. And adds new unit tests.
Filename:
MIME Type:
Creator:
Kirk Russell
Created:
2014-06-06 23:56:49 UTC
Size:
16.74 KB
patch
obsolete
>Index: Makefile >=================================================================== >--- Makefile (revision 266929) >+++ Makefile (working copy) >@@ -4,4 +4,8 @@ > DPADD= ${LIBUTIL} > LDADD= -lutil > >+.if ${MK_TESTS} != "no" >+SUBDIR+= tests >+.endif >+ > .include <bsd.prog.mk> >Index: tests/Makefile >=================================================================== >--- tests/Makefile (revision 0) >+++ tests/Makefile (working copy) >@@ -0,0 +1,13 @@ >+# $FreeBSD$ >+ >+TESTSDIR= ${TESTSBASE}/usr.bin/truncate >+ATF_TESTS_SH= truncate_debug_test truncate_test >+CLEANFILES+= truncate_debug_test.sh truncate_test.sh >+ >+truncate_debug_test.sh: base_test.func truncate_debug_test.func >+ cat ${.ALLSRC} > ${.TARGET} >+ >+truncate_test.sh: base_test.func truncate_test.func >+ cat ${.ALLSRC} > ${.TARGET} >+ >+.include <bsd.test.mk> >Index: tests/base_test.func >=================================================================== >--- tests/base_test.func (revision 0) >+++ tests/base_test.func (working copy) >@@ -0,0 +1,405 @@ >+# >+# Copyright 2014, Google Inc. All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions are >+# met: >+# >+# * Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# * Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# * Neither the name of Google Inc. nor the names of its >+# contributors may be used to endorse or promote products derived from >+# this software without specific written permission. >+# >+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ >+# Helper function that is always used to create and fill stderr.txt for these >+# tests. >+_custom_create_file() >+{ >+ # The first argument is a command. >+ # The second is just a string. >+ case "${1}" in >+ creat) > stderr.txt ;; >+ debug) [ "${DEBUG}" -a "${2}" ] && \ >+ printf "\t%s\n" "${2}" >> stderr.txt ;; >+ print) [ "${2}" ] && \ >+ printf "%s\n" "${2}" >> stderr.txt ;; >+ esac >+} >+ >+# Helper function that create the file stderr.txt that contains the string >+# passed in as the first argument. But only if -D mode is currently active. >+debug_create_stderr_file() >+{ >+ _custom_create_file creat >+ _custom_create_file debug "${1}" >+} >+ >+# Helper function that create the file stderr.txt that contains the string >+# passed in as the first argument. >+create_stderr_file() >+{ >+ _custom_create_file creat >+ _custom_create_file print "${1}" >+} >+ >+# Helper function that create the file stderr.txt that contains the expected >+# truncate utility usage message. >+create_stderr_usage_file() >+{ >+ _custom_create_file creat >+ _custom_create_file print "${1}" >+ _custom_create_file print \ >+ "usage: truncate [-c] -s [+|-]size[K|k|M|m|G|g|T|t] file ..." >+ _custom_create_file print " truncate [-c] -r rfile file ..." >+} >+ >+atf_test_case illegal_option >+illegal_option_head() >+{ >+ atf_set "descr" "Verifies that truncate exits >0 when passed an" \ >+ "invalid command line option" >+} >+illegal_option_body() >+{ >+ create_stderr_usage_file 'truncate: illegal option -- 7' >+ >+ # We expect the error message, with no new files. >+ atf_check -s not-exit:0 -e file:stderr.txt ${TRUNC} -7 -s0 output.txt >+ [ ! -e output.txt ] || atf_fail "output.txt should not exist" >+} >+ >+atf_test_case illegal_size >+illegal_size_head() >+{ >+ atf_set "descr" "Verifies that truncate exits >0 when passed an" \ >+ "invalid power of two convention" >+} >+illegal_size_body() >+{ >+ create_stderr_file "truncate: invalid size argument \`+1L'" >+ >+ # We expect the error message, with no new files. >+ atf_check -s not-exit:0 -e file:stderr.txt ${TRUNC} -s+1L output.txt >+ [ ! -e output.txt ] || atf_fail "output.txt should not exist" >+} >+ >+atf_test_case too_large_size >+too_large_size_head() >+{ >+ atf_set "descr" "Verifies that truncate exits >0 when passed an" \ >+ "a size that is INT64_MAX < size <= UINT64_MAX" >+} >+too_large_size_body() >+{ >+ create_stderr_file "truncate: invalid size argument \`8388608t'" >+ >+ # We expect the error message, with no new files. >+ atf_check -s not-exit:0 -e file:stderr.txt \ >+ ${TRUNC} -s8388608t output.txt >+ [ ! -e output.txt ] || atf_fail "output.txt should not exist" >+} >+ >+atf_test_case opt_c >+opt_c_head() >+{ >+ atf_set "descr" "Verifies that -c prevents creation of new files" >+} >+opt_c_body() >+{ >+ # No new files and truncate returns 0 as if this is a success. >+ atf_check ${TRUNC} -c -s 0 doesnotexist.txt >+ [ ! -e output.txt ] || atf_fail "doesnotexist.txt should not exist" >+ > reference >+ atf_check ${TRUNC} -c -r reference doesnotexist.txt >+ [ ! -e output.txt ] || atf_fail "doesnotexist.txt should not exist" >+ >+ debug_create_stderr_file "ftruncate(fd, 1)" >+ >+ # The existing file will be altered by truncate. >+ > exists.txt >+ atf_check -e file:stderr.txt ${TRUNC} -c -s1 exists.txt >+ [ -s exists.txt ] || atf_fail "exists.txt be larger than zero bytes" >+} >+ >+atf_test_case opt_rs >+opt_rs_head() >+{ >+ atf_set "descr" "Verifies that truncate command line flags" \ >+ "-s and -r cannot be specifed together" >+} >+opt_rs_body() >+{ >+ create_stderr_usage_file >+ >+ # Force an error due to the use of both -s and -r. >+ > afile >+ atf_check -s not-exit:0 -e file:stderr.txt ${TRUNC} -s0 -r afile afile >+} >+ >+atf_test_case opt_rs >+opt_rs_head() >+{ >+ atf_set "descr" "Verifies that truncate command line" \ >+ "flags -s and -r cannot be specifed together" >+} >+opt_rs_body() >+{ >+ create_stderr_usage_file >+ >+ # Force an error due to the use of both -s and -r. >+ > afile >+ atf_check -s not-exit:0 -e file:stderr.txt ${TRUNC} -s0 -r afile afile >+} >+ >+atf_test_case no_files >+no_files_head() >+{ >+ atf_set "descr" "Verifies that truncate needs a list of files on" \ >+ "the command line" >+} >+no_files_body() >+{ >+ create_stderr_usage_file >+ >+ # A list of files must be present on the command line. >+ atf_check -s not-exit:0 -e file:stderr.txt ${TRUNC} -s1 >+} >+ >+atf_test_case bad_refer >+bad_refer_head() >+{ >+ atf_set "descr" "Verifies that truncate needs a list of files on" \ >+ "the command line" >+} >+bad_refer_body() >+{ >+ create_stderr_file "truncate: afile: No such file or directory" >+ >+ # The reference file must exist before you try to use it. >+ atf_check -s not-exit:0 -e file:stderr.txt ${TRUNC} -r afile afile >+ [ ! -e afile ] || atf_fail "afile should not exist" >+} >+ >+atf_test_case bad_truncate cleanup >+bad_truncate_head() >+{ >+ atf_set "descr" "Verifies that truncate needs a list of files on" \ >+ "the command line" >+} >+bad_truncate_body() >+{ >+ create_stderr_file "truncate: exists.txt: Operation not permitted" >+ >+ # Trying to get the ftruncate() call to return -1. >+ > exists.txt >+ atf_check chflags uimmutable exists.txt >+ >+ atf_check -s not-exit:0 -e file:stderr.txt ${TRUNC} -s1 exists.txt >+} >+bad_truncate_cleanup() >+{ >+ chflags 0 exists.txt >+} >+ >+atf_test_case new_absolute_grow >+new_absolute_grow_head() >+{ >+ atf_set "descr" "Verifies truncate can make and grow a new 1m file" >+} >+new_absolute_grow_body() >+{ >+ debug_create_stderr_file "ftruncate(fd, 1024)" >+ >+ # Create a new file and grow it to 1024 bytes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s1k output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" >+ >+ debug_create_stderr_file "ftruncate(fd, 1048576)" >+ >+ # Grow the existing file to 1M. We are using absolute sizes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -c -s1M output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m" >+} >+ >+atf_test_case new_absolute_shrink >+new_absolute_shrink_head() >+{ >+ atf_set "descr" "Verifies that truncate can make and" \ >+ "shrink a new 1m file" >+} >+new_absolute_shrink_body() >+{ >+ debug_create_stderr_file "ftruncate(fd, 1048576)" >+ >+ # Create a new file and grow it to 1048576 bytes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s1M output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m" >+ >+ debug_create_stderr_file "ftruncate(fd, 1024)" >+ >+ # Shrink the existing file to 1k. We are using absolute sizes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s1k output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" >+} >+ >+atf_test_case new_relative_grow >+new_relative_grow_head() >+{ >+ atf_set "descr" "Verifies truncate can make and grow a new 1m file" >+} >+new_relative_grow_body() >+{ >+ debug_create_stderr_file "ftruncate(fd, 1024)" >+ >+ # Create a new file and grow it to 1024 bytes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s+1k output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" >+ >+ debug_create_stderr_file "ftruncate(fd, 1048576)" >+ >+ # Grow the existing file to 1M. We are using relative sizes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s+1047552 output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m" >+} >+ >+atf_test_case new_relative_shrink >+new_relative_shrink_head() >+{ >+ atf_set "descr" "Verifies truncate can make and shrink a new 1m file" >+} >+new_relative_shrink_body() >+{ >+ debug_create_stderr_file "ftruncate(fd, 1049600)" >+ >+ # Create a new file and grow it to 1049600 bytes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s+1049600 output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1049600 ] || atf_fail "expected file size of 1m" >+ >+ debug_create_stderr_file "ftruncate(fd, 1024)" >+ >+ # Shrink the existing file to 1k. We are using relative sizes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s-1M output.txt >+ atf_check -s exit:1 cmp -s output.txt /dev/zero >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" >+} >+ >+atf_test_case cannot_open >+cannot_open_head() >+{ >+ atf_set "descr" "Verifies truncate can make and grow a new 1m file" >+ atf_set "require.user" "unprivileged" >+} >+cannot_open_body() >+{ >+ # Create three files -- the middle file cannot allow writes. >+ > before >+ > 0000 >+ > after >+ atf_check chmod 0000 0000 >+ >+ # Create a file that contains the expected error message. >+ _custom_create_file creat >+ _custom_create_file debug "ftruncate(fd, 1024)" >+ _custom_create_file print "truncate: 0000: Permission denied" >+ _custom_create_file debug "ftruncate(fd, 1024)" >+ >+ # Create a new file and grow it to 1024 bytes. >+ atf_check -s not-exit:0 -e file:stderr.txt \ >+ ${TRUNC} -c -s1k before 0000 after >+ eval $(stat -s before) >+ [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" >+ eval $(stat -s after) >+ [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" >+ eval $(stat -s 0000) >+ [ ${st_size} -eq 0 ] || atf_fail "expected file size of zero" >+} >+ >+atf_test_case reference >+reference_head() >+{ >+ atf_set "descr" "Verifies that truncate can use a reference file" >+} >+reference_body() >+{ >+ # Create a 4 byte reference file. >+ printf "123\n" > reference >+ eval $(stat -s reference) >+ [ ${st_size} -eq 4 ] || atf_fail "reference file should be 4 bytes" >+ >+ debug_create_stderr_file "ftruncate(fd, 4)" >+ >+ # Create a new file and grow it to 4 bytes. >+ atf_check -e file:stderr.txt ${TRUNC} -r reference afile >+ eval $(stat -s afile) >+ [ ${st_size} -eq 4 ] || atf_fail "new file should also be 4 bytes" >+} >+ >+atf_test_case new_zero >+new_zero_head() >+{ >+ atf_set "descr" "Verifies truncate can make and grow zero byte file" >+} >+new_zero_body() >+{ >+ debug_create_stderr_file "ftruncate(fd, 0)" >+ >+ # Create a new file and grow it to zero bytes. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s0 output.txt >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 0 ] || atf_fail "expected file size of zero" >+ >+ # Pretend to grow the file. >+ atf_check -s exit:0 -e file:stderr.txt ${TRUNC} -s+0 output.txt >+ eval $(stat -s output.txt) >+ [ ${st_size} -eq 0 ] || atf_fail "expected file size of zero" >+} >+ >+atf_test_case negative >+negative_head() >+{ >+ atf_set "descr" "Verifies truncate treats negative sizes as zero" >+} >+negative_body() >+{ >+ # Create a 5 byte file. >+ printf "abcd\n" > afile >+ eval $(stat -s afile) >+ [ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes" >+ >+ debug_create_stderr_file "ftruncate(fd, 0)" >+ >+ # Create a new file and do a 100 byte negative relative shrink. >+ atf_check -e file:stderr.txt ${TRUNC} -s-100 afile >+ eval $(stat -s afile) >+ [ ${st_size} -eq 0 ] || atf_fail "new file should now be zero bytes" >+} >Index: tests/truncate_debug_test.func >=================================================================== >--- tests/truncate_debug_test.func (revision 0) >+++ tests/truncate_debug_test.func (working copy) >@@ -0,0 +1,22 @@ >+atf_init_test_cases() >+{ >+ TRUNC="truncate -D" >+ DEBUG="1" >+ >+ atf_add_test_case illegal_option >+ atf_add_test_case illegal_size >+ atf_add_test_case too_large_size >+ atf_add_test_case opt_c >+ atf_add_test_case opt_rs >+ atf_add_test_case no_files >+ atf_add_test_case bad_refer >+ atf_add_test_case bad_truncate >+ atf_add_test_case cannot_open >+ atf_add_test_case new_absolute_grow >+ atf_add_test_case new_absolute_shrink >+ atf_add_test_case new_relative_grow >+ atf_add_test_case new_relative_shrink >+ atf_add_test_case reference >+ atf_add_test_case new_zero >+ atf_add_test_case negative >+} >Index: tests/truncate_test.func >=================================================================== >--- tests/truncate_test.func (revision 0) >+++ tests/truncate_test.func (working copy) >@@ -0,0 +1,21 @@ >+atf_init_test_cases() >+{ >+ TRUNC="truncate" >+ >+ atf_add_test_case illegal_option >+ atf_add_test_case illegal_size >+ atf_add_test_case too_large_size >+ atf_add_test_case opt_c >+ atf_add_test_case opt_rs >+ atf_add_test_case no_files >+ atf_add_test_case bad_refer >+ atf_add_test_case bad_truncate >+ atf_add_test_case cannot_open >+ atf_add_test_case new_absolute_grow >+ atf_add_test_case new_absolute_shrink >+ atf_add_test_case new_relative_grow >+ atf_add_test_case new_relative_shrink >+ atf_add_test_case reference >+ atf_add_test_case new_zero >+ atf_add_test_case negative >+} >Index: truncate.c >=================================================================== >--- truncate.c (revision 266929) >+++ truncate.c (working copy) >@@ -36,6 +36,7 @@ > #include <err.h> > #include <errno.h> > #include <fcntl.h> >+#include <inttypes.h> > #include <stdio.h> > #include <stdlib.h> > #include <unistd.h> >@@ -45,17 +46,29 @@ > static void usage(void); > > static int no_create; >+static int do_debug; > static int do_relative; > static int do_refer; > static int got_size; > >+inline int >+do_ftruncate(int fd, off_t length) >+{ >+ if (do_debug) >+ fprintf(stderr, >+ "\tftruncate(fd, %"PRIdMAX")\n", >+ (uintmax_t)length); >+ >+ return ftruncate(fd, length); >+} >+ > int > main(int argc, char **argv) > { > struct stat sb; > mode_t omode; >- off_t oflow, rsize, tsize; >- int64_t sz; >+ off_t oflow, rsize, sz, tsize; >+ uint64_t usz; > int ch, error, fd, oflags; > char *fname, *rname; > >@@ -63,8 +76,11 @@ > rsize = tsize = sz = 0; > error = 0; > rname = NULL; >- while ((ch = getopt(argc, argv, "cr:s:")) != -1) >+ while ((ch = getopt(argc, argv, "Dcr:s:")) != -1) > switch (ch) { >+ case 'D': >+ do_debug = 1; >+ break; > case 'c': > no_create = 1; > break; >@@ -73,11 +89,13 @@ > rname = optarg; > break; > case 's': >- if (expand_number(optarg, &sz) == -1) >+ do_relative = *optarg == '+' || *optarg == '-'; >+ if ((expand_number(do_relative ? optarg+1 : optarg, >+ &usz) == -1) || ((off_t)usz < 0)) > errx(EXIT_FAILURE, > "invalid size argument `%s'", optarg); >- if (*optarg == '+' || *optarg == '-') >- do_relative = 1; >+ >+ sz = (*optarg == '-') ? -(off_t)usz : (off_t)usz; > got_size = 1; > break; > default: >@@ -138,7 +156,7 @@ > if (tsize < 0) > tsize = 0; > >- if (ftruncate(fd, tsize) == -1) { >+ if (do_ftruncate(fd, tsize) == -1) { > warn("%s", fname); > error++; > continue;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 190735
: 143473 |
143501