1 /*
2 * Copyright 2020 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef LIBGAV1_TESTS_FUZZER_FUZZER_TEMP_FILE_H_
18 #define LIBGAV1_TESTS_FUZZER_FUZZER_TEMP_FILE_H_
19
20 // Adapter utility from fuzzer input to a temporary file, for fuzzing APIs that
21 // require a file instead of an input buffer.
22
23 #include <limits.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 // Pure-C interface for creating and cleaning up temporary files.
31
fuzzer_get_tmpfile_with_suffix(const uint8_t * data,size_t size,const char * suffix)32 static char* fuzzer_get_tmpfile_with_suffix(const uint8_t* data, size_t size,
33 const char* suffix) {
34 if (suffix == NULL) { // NOLINT (this could be a C compilation unit)
35 suffix = "";
36 }
37 const size_t suffix_len = strlen(suffix);
38 if (suffix_len > INT_MAX) { // mkstemps takes int for suffixlen param
39 perror("Suffix too long");
40 abort();
41 }
42
43 #ifdef __ANDROID__
44 const char* leading_temp_path =
45 "/data/local/tmp/generate_temporary_file.XXXXXX";
46 #else
47 const char* leading_temp_path = "/tmp/generate_temporary_file.XXXXXX";
48 #endif
49 const size_t buffer_sz = strlen(leading_temp_path) + suffix_len + 1;
50 char* filename_buffer =
51 (char*)malloc(buffer_sz); // NOLINT (this could be a C compilation unit)
52 if (!filename_buffer) {
53 perror("Failed to allocate file name buffer.");
54 abort();
55 }
56
57 if (snprintf(filename_buffer, buffer_sz, "%s%s", leading_temp_path, suffix) >=
58 buffer_sz) {
59 perror("File name buffer too short.");
60 abort();
61 }
62
63 const int file_descriptor = mkstemps(filename_buffer, suffix_len);
64 if (file_descriptor < 0) {
65 perror("Failed to make temporary file.");
66 abort();
67 }
68 FILE* file = fdopen(file_descriptor, "wb");
69 if (!file) {
70 perror("Failed to open file descriptor.");
71 close(file_descriptor);
72 abort();
73 }
74 const size_t bytes_written = fwrite(data, sizeof(uint8_t), size, file);
75 if (bytes_written < size) {
76 close(file_descriptor);
77 fprintf(stderr, "Failed to write all bytes to file (%zu out of %zu)",
78 bytes_written, size);
79 abort();
80 }
81 fclose(file);
82 return filename_buffer;
83 }
84
fuzzer_get_tmpfile(const uint8_t * data,size_t size)85 static char* fuzzer_get_tmpfile(
86 const uint8_t* data,
87 size_t size) { // NOLINT (people include this .inc file directly)
88 return fuzzer_get_tmpfile_with_suffix(data, size, NULL); // NOLINT
89 }
90
fuzzer_release_tmpfile(char * filename)91 static void fuzzer_release_tmpfile(char* filename) {
92 if (unlink(filename) != 0) {
93 perror("WARNING: Failed to delete temporary file.");
94 }
95 free(filename);
96 }
97
98 // C++ RAII object for creating temporary files.
99
100 #ifdef __cplusplus
101 class FuzzerTemporaryFile {
102 public:
FuzzerTemporaryFile(const uint8_t * data,size_t size)103 FuzzerTemporaryFile(const uint8_t* data, size_t size)
104 : original_filename_(fuzzer_get_tmpfile(data, size)) {
105 filename_ = strdup(original_filename_);
106 if (!filename_) {
107 perror("Failed to allocate file name copy.");
108 abort();
109 }
110 }
111
FuzzerTemporaryFile(const uint8_t * data,size_t size,const char * suffix)112 FuzzerTemporaryFile(const uint8_t* data, size_t size, const char* suffix)
113 : original_filename_(fuzzer_get_tmpfile_with_suffix(data, size, suffix)) {
114 filename_ = strdup(original_filename_);
115 if (!filename_) {
116 perror("Failed to allocate file name copy.");
117 abort();
118 }
119 }
120
~FuzzerTemporaryFile()121 ~FuzzerTemporaryFile() {
122 free(filename_);
123 fuzzer_release_tmpfile(original_filename_);
124 }
125
126 FuzzerTemporaryFile(const FuzzerTemporaryFile& other) = delete;
127 FuzzerTemporaryFile operator=(const FuzzerTemporaryFile& other) = delete;
128
129 FuzzerTemporaryFile(const FuzzerTemporaryFile&& other) = delete;
130 FuzzerTemporaryFile operator=(const FuzzerTemporaryFile&& other) = delete;
131
filename()132 const char* filename() const { return filename_; }
133
134 // Returns a mutable pointer to the file name. Should be used sparingly, only
135 // in case the fuzzed API demands it or when making a mutable copy is
136 // inconvenient (e.g., in auto-generated code).
mutable_filename()137 char* mutable_filename() const { return filename_; }
138
139 private:
140 char* original_filename_;
141
142 // A mutable copy of the original filename, returned by the accessor. This
143 // guarantees that the original filename can always be used to release the
144 // temporary path.
145 char* filename_;
146 };
147 #endif // __cplusplus
148 #endif // LIBGAV1_TESTS_FUZZER_FUZZER_TEMP_FILE_H_
149