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 #ifdef _WIN32
29 #include <io.h>
30 #include <windows.h>
31
32 #define strdup _strdup
33 #define unlink _unlink
34 #else
35 #include <unistd.h>
36 #endif // _WIN32
37
38 // Pure-C interface for creating and cleaning up temporary files.
39
fuzzer_get_tmpfile_with_suffix(const uint8_t * data,size_t size,const char * suffix)40 static char* fuzzer_get_tmpfile_with_suffix(const uint8_t* data, size_t size,
41 const char* suffix) {
42 #ifdef _WIN32
43 // GetTempPathA generates '<path>\<pre><uuuu>.TMP'.
44 (void)suffix; // NOLINT (this could be a C compilation unit)
45 char temp_path[MAX_PATH];
46 const DWORD ret = GetTempPathA(MAX_PATH, temp_path);
47 if (ret == 0 || ret > MAX_PATH) {
48 fprintf(stderr, "Error getting temporary directory name: %lu\n",
49 GetLastError());
50 abort();
51 }
52 char* filename_buffer =
53 (char*)malloc(MAX_PATH); // NOLINT (this could be a C compilation unit)
54 if (!filename_buffer) {
55 perror("Failed to allocate file name buffer.");
56 abort();
57 }
58 if (GetTempFileNameA(temp_path, "ftf", /*uUnique=*/0, filename_buffer) == 0) {
59 fprintf(stderr, "Error getting temporary file name: %lu\n", GetLastError());
60 abort();
61 }
62 #if defined(_MSC_VER) || defined(MINGW_HAS_SECURE_API)
63 FILE* file;
64 const errno_t err = fopen_s(&file, filename_buffer, "wb");
65 if (err != 0) file = NULL; // NOLINT (this could be a C compilation unit)
66 #else
67 FILE* file = fopen(filename_buffer, "wb");
68 #endif
69 if (!file) {
70 perror("Failed to open file.");
71 abort();
72 }
73 #else // !_WIN32
74 if (suffix == NULL) { // NOLINT (this could be a C compilation unit)
75 suffix = "";
76 }
77 const size_t suffix_len = strlen(suffix);
78 if (suffix_len > INT_MAX) { // mkstemps takes int for suffixlen param
79 perror("Suffix too long");
80 abort();
81 }
82
83 #ifdef __ANDROID__
84 const char* leading_temp_path =
85 "/data/local/tmp/generate_temporary_file.XXXXXX";
86 #else
87 const char* leading_temp_path = "/tmp/generate_temporary_file.XXXXXX";
88 #endif
89 const size_t buffer_sz = strlen(leading_temp_path) + suffix_len + 1;
90 char* filename_buffer =
91 (char*)malloc(buffer_sz); // NOLINT (this could be a C compilation unit)
92 if (!filename_buffer) {
93 perror("Failed to allocate file name buffer.");
94 abort();
95 }
96
97 if (snprintf(filename_buffer, buffer_sz, "%s%s", leading_temp_path, suffix) >=
98 (int)buffer_sz) { // NOLINT (this could be a C compilation unit)
99 perror("File name buffer too short.");
100 abort();
101 }
102
103 const int file_descriptor = mkstemps(filename_buffer, suffix_len);
104 if (file_descriptor < 0) {
105 perror("Failed to make temporary file.");
106 abort();
107 }
108 FILE* file = fdopen(file_descriptor, "wb");
109 if (!file) {
110 perror("Failed to open file descriptor.");
111 close(file_descriptor);
112 abort();
113 }
114 #endif // _WIN32
115 const size_t bytes_written = fwrite(data, sizeof(uint8_t), size, file);
116 if (bytes_written < size) {
117 fclose(file);
118 fprintf(stderr, "Failed to write all bytes to file (%zu out of %zu)",
119 bytes_written, size);
120 abort();
121 }
122 fclose(file);
123 return filename_buffer;
124 }
125
fuzzer_get_tmpfile(const uint8_t * data,size_t size)126 static char* fuzzer_get_tmpfile(
127 const uint8_t* data,
128 size_t size) { // NOLINT (people include this .inc file directly)
129 return fuzzer_get_tmpfile_with_suffix(data, size, NULL); // NOLINT
130 }
131
fuzzer_release_tmpfile(char * filename)132 static void fuzzer_release_tmpfile(char* filename) {
133 if (unlink(filename) != 0) {
134 perror("WARNING: Failed to delete temporary file.");
135 }
136 free(filename);
137 }
138
139 // C++ RAII object for creating temporary files.
140
141 #ifdef __cplusplus
142 class FuzzerTemporaryFile {
143 public:
FuzzerTemporaryFile(const uint8_t * data,size_t size)144 FuzzerTemporaryFile(const uint8_t* data, size_t size)
145 : original_filename_(fuzzer_get_tmpfile(data, size)) {
146 filename_ = strdup(original_filename_);
147 if (!filename_) {
148 perror("Failed to allocate file name copy.");
149 abort();
150 }
151 }
152
FuzzerTemporaryFile(const uint8_t * data,size_t size,const char * suffix)153 FuzzerTemporaryFile(const uint8_t* data, size_t size, const char* suffix)
154 : original_filename_(fuzzer_get_tmpfile_with_suffix(data, size, suffix)) {
155 filename_ = strdup(original_filename_);
156 if (!filename_) {
157 perror("Failed to allocate file name copy.");
158 abort();
159 }
160 }
161
~FuzzerTemporaryFile()162 ~FuzzerTemporaryFile() {
163 free(filename_);
164 fuzzer_release_tmpfile(original_filename_);
165 }
166
167 FuzzerTemporaryFile(const FuzzerTemporaryFile& other) = delete;
168 FuzzerTemporaryFile operator=(const FuzzerTemporaryFile& other) = delete;
169
170 FuzzerTemporaryFile(const FuzzerTemporaryFile&& other) = delete;
171 FuzzerTemporaryFile operator=(const FuzzerTemporaryFile&& other) = delete;
172
filename()173 const char* filename() const { return filename_; }
174
175 // Returns a mutable pointer to the file name. Should be used sparingly, only
176 // in case the fuzzed API demands it or when making a mutable copy is
177 // inconvenient (e.g., in auto-generated code).
mutable_filename()178 char* mutable_filename() const { return filename_; }
179
180 private:
181 char* original_filename_;
182
183 // A mutable copy of the original filename, returned by the accessor. This
184 // guarantees that the original filename can always be used to release the
185 // temporary path.
186 char* filename_;
187 };
188 #endif // __cplusplus
189 #endif // LIBGAV1_TESTS_FUZZER_FUZZER_TEMP_FILE_H_
190