Fix places where a negative return from ftello could be used as invalid input
[asterisk/asterisk.git] / funcs / func_env.c
index f5814ae..f413607 100644 (file)
  * \ingroup functions
  */
 
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@@ -75,7 +79,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </synopsis>
                <syntax>
                        <parameter name="filename" required="true" />
-                       </parameter>
                        <parameter name="offset">
                                <para>Maybe specified as any number. If negative, <replaceable>offset</replaceable> specifies the number
                                of bytes back from the end of the file.</para>
@@ -471,7 +474,6 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
        int64_t flength, i; /* iterator needs to be signed, so it can go negative and terminate the loop */
        int64_t offset_offset = -1, length_offset = -1;
        char dos_state = 0;
-       size_t readlen;
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(filename);
                AST_APP_ARG(offset);
@@ -498,17 +500,26 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
                        return 0;
                }
 
-               fseeko(ff, 0, SEEK_END);
+               if (fseeko(ff, 0, SEEK_END) < 0) {
+                       ast_log(LOG_ERROR, "Cannot seek to end of '%s': %s\n", args.filename, strerror(errno));
+                       fclose(ff);
+                       return -1;
+               }
                flength = ftello(ff);
 
                if (offset < 0) {
                        fseeko(ff, offset, SEEK_END);
-                       offset = ftello(ff);
+                       if ((offset = ftello(ff)) < 0) {
+                               ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
+                               fclose(ff);
+                               return -1;
+                       }
                }
                if (length < 0) {
                        fseeko(ff, length, SEEK_END);
                        if ((length = ftello(ff)) - offset < 0) {
                                /* Eliminates all results */
+                               fclose(ff);
                                return -1;
                        }
                } else if (length == LLONG_MAX) {
@@ -535,6 +546,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
 
                        ast_str_append_substr(buf, len, fbuf, toappend);
                }
+               fclose(ff);
                return 0;
        }
 
@@ -573,6 +585,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
                fclose(ff);
                return -1;
        }
+
        flength = ftello(ff);
 
        if (length == LLONG_MAX) {
@@ -591,7 +604,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
                                ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
                        }
                        end = fread(fbuf, 1, sizeof(fbuf), ff);
-                       for (pos = end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
+                       for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos > fbuf - 1; pos--) {
                                LINE_COUNTER(pos, format, count);
 
                                if (length < 0 && count * -1 == length) {
@@ -626,7 +639,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
                                /* Don't let previous values influence current counts, due to short reads */
                                memset(fbuf, 0, sizeof(fbuf));
                        }
-                       if (fread(fbuf, 1, sizeof(fbuf), ff) && !feof(ff)) {
+                       if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
                                ast_log(LOG_ERROR, "Short read?!!\n");
                                fclose(ff);
                                return -1;
@@ -678,8 +691,10 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
                ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
                for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
                        char *pos;
-                       if ((readlen = fread(fbuf, 1, sizeof(fbuf), ff)) < sizeof(fbuf) && !feof(ff)) {
+                       if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
                                ast_log(LOG_ERROR, "Short read?!!\n");
+                               fclose(ff);
+                               return -1;
                        }
                        for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
                                LINE_COUNTER(pos, format, current_length);
@@ -698,6 +713,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
                }
        }
 
+       fclose(ff);
        return 0;
 }
 
@@ -719,7 +735,7 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
        );
        int64_t offset = 0, length = LLONG_MAX;
        off_t flength, vlength;
-       size_t foplen;
+       size_t foplen = 0;
        FILE *ff;
 
        AST_STANDARD_APP_ARGS(args, data);
@@ -768,9 +784,15 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
 
                if (offset < 0) {
                        if (fseeko(ff, offset, SEEK_END)) {
-                               ast_log(LOG_ERROR, "Cannot seek to offset: %s\n", strerror(errno));
+                               ast_log(LOG_ERROR, "Cannot seek to offset of '%s': %s\n", args.filename, strerror(errno));
+                               fclose(ff);
+                               return -1;
+                       }
+                       if ((offset = ftello(ff)) < 0) {
+                               ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
+                               fclose(ff);
+                               return -1;
                        }
-                       offset = ftello(ff);
                }
 
                if (length < 0) {
@@ -785,7 +807,7 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
                fseeko(ff, offset, SEEK_SET);
 
                ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
-                       args.offset, offset, args.length, length, vlength, flength);
+                       S_OR(args.offset, "(null)"), offset, S_OR(args.length, "(null)"), length, vlength, flength);
 
                if (length == vlength) {
                        /* Simplest case, a straight replace */
@@ -818,6 +840,11 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
                                if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
                                        ast_log(LOG_ERROR, "Short write?!!\n");
                                }
+                               /* Seek to where we stopped reading */
+                               if (fseeko(ff, cur + sizeof(fbuf), SEEK_SET) < 0) {
+                                       /* Only reason for seek to fail is EOF */
+                                       break;
+                               }
                        }
                        fclose(ff);
                        if (truncate(args.filename, flength - (length - vlength))) {
@@ -876,8 +903,12 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
                        /* Write out the value, then write just up until where we last moved some data */
                        if (fwrite(value, 1, vlength, ff) < vlength) {
                                ast_log(LOG_ERROR, "Short write?!!\n");
-                       } else if (fwrite(fbuf, 1, (foplen = lastwritten - ftello(ff)), ff) < foplen) {
-                               ast_log(LOG_ERROR, "Short write?!!\n");
+                       } else {
+                               off_t curpos = ftello(ff);
+                               foplen = lastwritten - curpos;
+                               if (fwrite(fbuf, 1, foplen, ff) < foplen) {
+                                       ast_log(LOG_ERROR, "Short write?!!\n");
+                               }
                        }
                        fclose(ff);
                }
@@ -923,10 +954,13 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
                        } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
                                ast_log(LOG_ERROR, "Short write?!!\n");
                        }
-                       truncsize = ftello(ff);
+                       if ((truncsize = ftello(ff)) < 0) {
+                               ast_log(AST_LOG_ERROR, "Unable to determine truncate position of '%s': %s\n", args.filename, strerror(errno));
+                       }
                        fclose(ff);
-                       if (truncate(args.filename, truncsize)) {
-                               ast_log(LOG_ERROR, "Unable to truncate file: %s\n", strerror(errno));
+                       if (truncsize >= 0 && truncate(args.filename, truncsize)) {
+                               ast_log(LOG_ERROR, "Unable to truncate file '%s': %s\n", args.filename, strerror(errno));
+                               return -1;
                        }
                } else {
                        int64_t offset_offset = (offset == 0 ? 0 : -1), length_offset = -1, flength, i, current_length = 0;
@@ -948,7 +982,11 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
                                fclose(ff);
                                return -1;
                        }
-                       flength = ftello(ff);
+                       if ((flength = ftello(ff)) < 0) {
+                               ast_log(AST_LOG_ERROR, "Cannot determine end position of file '%s': %s\n", args.filename, strerror(errno));
+                               fclose(ff);
+                               return -1;
+                       }
 
                        /* For negative offset and/or negative length */
                        if (offset < 0 || length < 0) {
@@ -1093,6 +1131,11 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
                                        return -1;
                                }
                                while ((cur = ftello(ff)) < flength) {
+                                       if (cur < 0) {
+                                               ast_log(AST_LOG_ERROR, "Unable to determine last write position for '%s': %s\n", args.filename, strerror(errno));
+                                               fclose(ff);
+                                               return -1;
+                                       }
                                        fseeko(ff, length_length - vlen, SEEK_CUR);
                                        if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
                                                ast_log(LOG_ERROR, "Short read?!!\n");
@@ -1153,8 +1196,12 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
                                        ast_log(LOG_ERROR, "Short write?!!\n");
                                        fclose(ff);
                                        return -1;
-                               } else if (fwrite(fbuf, 1, (foplen = lastwritten - ftello(ff)), ff) < foplen) {
-                                       ast_log(LOG_ERROR, "Short write?!!\n");
+                               } else {
+                                       off_t curpos = ftello(ff);
+                                       foplen = lastwritten - curpos;
+                                       if (fwrite(fbuf, 1, foplen, ff) < foplen) {
+                                               ast_log(LOG_ERROR, "Short write?!!\n");
+                                       }
                                }
                                fclose(ff);
                        }