1 // Copyright (c) 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "sql/vfs_wrapper.h"
6 
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10 
11 #include "base/check_op.h"
12 #include "base/debug/leak_annotations.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/notreached.h"
17 #include "base/strings/string_piece.h"
18 #include "build/build_config.h"
19 
20 #if defined(OS_MAC)
21 #include "base/mac/mac_util.h"
22 #endif
23 
24 #if defined(OS_FUCHSIA)
25 #include "sql/vfs_wrapper_fuchsia.h"
26 #endif
27 
28 namespace sql {
29 namespace {
30 
31 // https://www.sqlite.org/vfs.html - documents the overall VFS system.
32 //
33 // https://www.sqlite.org/c3ref/vfs.html - VFS methods.  This code tucks the
34 // wrapped VFS pointer into the wrapper's pAppData pointer.
35 //
36 // https://www.sqlite.org/c3ref/file.html - instance of an open file.  This code
37 // allocates a VfsFile for this, which contains a pointer to the wrapped file.
38 // Idiomatic SQLite would take the wrapped VFS szOsFile and increase it to store
39 // additional data as a prefix.
40 
GetWrappedVfs(sqlite3_vfs * wrapped_vfs)41 sqlite3_vfs* GetWrappedVfs(sqlite3_vfs* wrapped_vfs) {
42   return static_cast<sqlite3_vfs*>(wrapped_vfs->pAppData);
43 }
44 
AsVfsFile(sqlite3_file * wrapper_file)45 VfsFile* AsVfsFile(sqlite3_file* wrapper_file) {
46   return reinterpret_cast<VfsFile*>(wrapper_file);
47 }
48 
GetWrappedFile(sqlite3_file * wrapper_file)49 sqlite3_file* GetWrappedFile(sqlite3_file* wrapper_file) {
50   return AsVfsFile(wrapper_file)->wrapped_file;
51 }
52 
Close(sqlite3_file * sqlite_file)53 int Close(sqlite3_file* sqlite_file)
54 {
55   VfsFile* file = AsVfsFile(sqlite_file);
56 
57 #if defined(OS_FUCHSIA)
58   FuchsiaVfsUnlock(sqlite_file, SQLITE_LOCK_NONE);
59 #endif
60 
61   int r = file->wrapped_file->pMethods->xClose(file->wrapped_file);
62   sqlite3_free(file->wrapped_file);
63 
64   // Memory will be freed with sqlite3_free(), so the destructor needs to be
65   // called explicitly.
66   file->~VfsFile();
67   memset(file, '\0', sizeof(*file));
68   return r;
69 }
70 
Read(sqlite3_file * sqlite_file,void * buf,int amt,sqlite3_int64 ofs)71 int Read(sqlite3_file* sqlite_file, void* buf, int amt, sqlite3_int64 ofs)
72 {
73   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
74   return wrapped_file->pMethods->xRead(wrapped_file, buf, amt, ofs);
75 }
76 
Write(sqlite3_file * sqlite_file,const void * buf,int amt,sqlite3_int64 ofs)77 int Write(sqlite3_file* sqlite_file, const void* buf, int amt,
78           sqlite3_int64 ofs)
79 {
80   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
81   return wrapped_file->pMethods->xWrite(wrapped_file, buf, amt, ofs);
82 }
83 
Truncate(sqlite3_file * sqlite_file,sqlite3_int64 size)84 int Truncate(sqlite3_file* sqlite_file, sqlite3_int64 size)
85 {
86   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
87   return wrapped_file->pMethods->xTruncate(wrapped_file, size);
88 }
89 
Sync(sqlite3_file * sqlite_file,int flags)90 int Sync(sqlite3_file* sqlite_file, int flags)
91 {
92   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
93   return wrapped_file->pMethods->xSync(wrapped_file, flags);
94 }
95 
FileSize(sqlite3_file * sqlite_file,sqlite3_int64 * size)96 int FileSize(sqlite3_file* sqlite_file, sqlite3_int64* size)
97 {
98   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
99   return wrapped_file->pMethods->xFileSize(wrapped_file, size);
100 }
101 
102 #if !defined(OS_FUCHSIA)
103 
Lock(sqlite3_file * sqlite_file,int file_lock)104 int Lock(sqlite3_file* sqlite_file, int file_lock)
105 {
106   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
107   return wrapped_file->pMethods->xLock(wrapped_file, file_lock);
108 }
109 
Unlock(sqlite3_file * sqlite_file,int file_lock)110 int Unlock(sqlite3_file* sqlite_file, int file_lock)
111 {
112   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
113   return wrapped_file->pMethods->xUnlock(wrapped_file, file_lock);
114 }
115 
CheckReservedLock(sqlite3_file * sqlite_file,int * result)116 int CheckReservedLock(sqlite3_file* sqlite_file, int* result)
117 {
118   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
119   return wrapped_file->pMethods->xCheckReservedLock(wrapped_file, result);
120 }
121 
122 #endif  // !defined(OS_FUCHSIA)
123 
FileControl(sqlite3_file * sqlite_file,int op,void * arg)124 int FileControl(sqlite3_file* sqlite_file, int op, void* arg)
125 {
126   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
127   return wrapped_file->pMethods->xFileControl(wrapped_file, op, arg);
128 }
129 
SectorSize(sqlite3_file * sqlite_file)130 int SectorSize(sqlite3_file* sqlite_file)
131 {
132   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
133   return wrapped_file->pMethods->xSectorSize(wrapped_file);
134 }
135 
DeviceCharacteristics(sqlite3_file * sqlite_file)136 int DeviceCharacteristics(sqlite3_file* sqlite_file)
137 {
138   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
139   return wrapped_file->pMethods->xDeviceCharacteristics(wrapped_file);
140 }
141 
ShmMap(sqlite3_file * sqlite_file,int region,int size,int extend,void volatile ** pp)142 int ShmMap(sqlite3_file *sqlite_file, int region, int size,
143            int extend, void volatile **pp) {
144   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
145   return wrapped_file->pMethods->xShmMap(
146       wrapped_file, region, size, extend, pp);
147 }
148 
ShmLock(sqlite3_file * sqlite_file,int ofst,int n,int flags)149 int ShmLock(sqlite3_file *sqlite_file, int ofst, int n, int flags) {
150   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
151   return wrapped_file->pMethods->xShmLock(wrapped_file, ofst, n, flags);
152 }
153 
ShmBarrier(sqlite3_file * sqlite_file)154 void ShmBarrier(sqlite3_file *sqlite_file) {
155   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
156   wrapped_file->pMethods->xShmBarrier(wrapped_file);
157 }
158 
ShmUnmap(sqlite3_file * sqlite_file,int del)159 int ShmUnmap(sqlite3_file *sqlite_file, int del) {
160   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
161   return wrapped_file->pMethods->xShmUnmap(wrapped_file, del);
162 }
163 
Fetch(sqlite3_file * sqlite_file,sqlite3_int64 off,int amt,void ** pp)164 int Fetch(sqlite3_file *sqlite_file, sqlite3_int64 off, int amt, void **pp) {
165   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
166   return wrapped_file->pMethods->xFetch(wrapped_file, off, amt, pp);
167 }
168 
Unfetch(sqlite3_file * sqlite_file,sqlite3_int64 off,void * p)169 int Unfetch(sqlite3_file *sqlite_file, sqlite3_int64 off, void *p) {
170   sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
171   return wrapped_file->pMethods->xUnfetch(wrapped_file, off, p);
172 }
173 
Open(sqlite3_vfs * vfs,const char * file_name,sqlite3_file * wrapper_file,int desired_flags,int * used_flags)174 int Open(sqlite3_vfs* vfs, const char* file_name, sqlite3_file* wrapper_file,
175          int desired_flags, int* used_flags) {
176   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
177 
178   sqlite3_file* wrapped_file = static_cast<sqlite3_file*>(
179       sqlite3_malloc(wrapped_vfs->szOsFile));
180   if (!wrapped_file)
181     return SQLITE_NOMEM;
182 
183   // NOTE(shess): SQLite's unixOpen() makes assumptions about the structure of
184   // |file_name|.  Do not pass a local copy, here, only the passed-in value.
185   int rc = wrapped_vfs->xOpen(wrapped_vfs,
186                               file_name, wrapped_file,
187                               desired_flags, used_flags);
188   if (rc != SQLITE_OK) {
189     sqlite3_free(wrapped_file);
190     return rc;
191   }
192   // NOTE(shess): Any early exit from here needs to call xClose() on
193   // |wrapped_file|.
194 
195 #if defined(OS_MAC)
196   // When opening journal files, propagate time-machine exclusion from db.
197   static int kJournalFlags =
198       SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL |
199       SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL;
200   if (file_name && (desired_flags & kJournalFlags)) {
201     // https://www.sqlite.org/c3ref/vfs.html indicates that the journal path
202     // will have a suffix separated by "-" from the main database file name.
203     base::StringPiece file_name_string_piece(file_name);
204     size_t dash_index = file_name_string_piece.rfind('-');
205     if (dash_index != base::StringPiece::npos) {
206       base::StringPiece db_name(file_name, dash_index);
207       if (base::mac::GetFileBackupExclusion(base::FilePath(db_name))) {
208         base::mac::SetFileBackupExclusion(
209             base::FilePath(file_name_string_piece));
210       }
211     }
212   }
213 #endif
214 
215   // |iVersion| determines what methods SQLite may call on the instance.
216   // Having the methods which can't be proxied return an error may cause SQLite
217   // to operate differently than if it didn't call those methods at all. To be
218   // on the safe side, the wrapper sqlite3_io_methods version perfectly matches
219   // the version of the wrapped files.
220   //
221   // At a first glance, it might be tempting to simplify the code by
222   // restricting wrapping support to VFS version 3. However, this might fail on
223   // Mac.
224   //
225   // On Mac, SQLite built with SQLITE_ENABLE_LOCKING_STYLE ends up using a VFS
226   // that dynamically dispatches between a few variants of sqlite3_io_methods,
227   // based on whether the opened database is on a local or on a remote (AFS,
228   // NFS) filesystem. Some variants return a VFS version 1 structure.
229   VfsFile* file = AsVfsFile(wrapper_file);
230 
231   // Call constructor explicitly since the memory is already allocated.
232   new (file) VfsFile();
233 
234   file->wrapped_file = wrapped_file;
235 
236 #if defined(OS_FUCHSIA)
237   file->file_name = file_name;
238   file->lock_level = SQLITE_LOCK_NONE;
239 #endif
240 
241   if (wrapped_file->pMethods->iVersion == 1) {
242     static const sqlite3_io_methods io_methods = {
243       1,
244       Close,
245       Read,
246       Write,
247       Truncate,
248       Sync,
249       FileSize,
250 #if !defined(OS_FUCHSIA)
251       Lock,
252       Unlock,
253       CheckReservedLock,
254 #else
255       FuchsiaVfsLock,
256       FuchsiaVfsUnlock,
257       FuchsiaVfsCheckReservedLock,
258 #endif
259       FileControl,
260       SectorSize,
261       DeviceCharacteristics,
262     };
263     file->methods = &io_methods;
264   } else if (wrapped_file->pMethods->iVersion == 2) {
265     static const sqlite3_io_methods io_methods = {
266       2,
267       Close,
268       Read,
269       Write,
270       Truncate,
271       Sync,
272       FileSize,
273 #if !defined(OS_FUCHSIA)
274       Lock,
275       Unlock,
276       CheckReservedLock,
277 #else
278       FuchsiaVfsLock,
279       FuchsiaVfsUnlock,
280       FuchsiaVfsCheckReservedLock,
281 #endif
282       FileControl,
283       SectorSize,
284       DeviceCharacteristics,
285       // Methods above are valid for version 1.
286       ShmMap,
287       ShmLock,
288       ShmBarrier,
289       ShmUnmap,
290     };
291     file->methods = &io_methods;
292   } else {
293     static const sqlite3_io_methods io_methods = {
294       3,
295       Close,
296       Read,
297       Write,
298       Truncate,
299       Sync,
300       FileSize,
301 #if !defined(OS_FUCHSIA)
302       Lock,
303       Unlock,
304       CheckReservedLock,
305 #else
306       FuchsiaVfsLock,
307       FuchsiaVfsUnlock,
308       FuchsiaVfsCheckReservedLock,
309 #endif
310       FileControl,
311       SectorSize,
312       DeviceCharacteristics,
313       // Methods above are valid for version 1.
314       ShmMap,
315       ShmLock,
316       ShmBarrier,
317       ShmUnmap,
318       // Methods above are valid for version 2.
319       Fetch,
320       Unfetch,
321     };
322     file->methods = &io_methods;
323   }
324   return SQLITE_OK;
325 }
326 
Delete(sqlite3_vfs * vfs,const char * file_name,int sync_dir)327 int Delete(sqlite3_vfs* vfs, const char* file_name, int sync_dir) {
328   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
329   return wrapped_vfs->xDelete(wrapped_vfs, file_name, sync_dir);
330 }
331 
Access(sqlite3_vfs * vfs,const char * file_name,int flag,int * res)332 int Access(sqlite3_vfs* vfs, const char* file_name, int flag, int* res) {
333   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
334   return wrapped_vfs->xAccess(wrapped_vfs, file_name, flag, res);
335 }
336 
FullPathname(sqlite3_vfs * vfs,const char * relative_path,int buf_size,char * absolute_path)337 int FullPathname(sqlite3_vfs* vfs, const char* relative_path,
338                  int buf_size, char* absolute_path) {
339   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
340   return wrapped_vfs->xFullPathname(
341       wrapped_vfs, relative_path, buf_size, absolute_path);
342 }
343 
Randomness(sqlite3_vfs * vfs,int buf_size,char * buffer)344 int Randomness(sqlite3_vfs* vfs, int buf_size, char* buffer) {
345   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
346   return wrapped_vfs->xRandomness(wrapped_vfs, buf_size, buffer);
347 }
348 
Sleep(sqlite3_vfs * vfs,int microseconds)349 int Sleep(sqlite3_vfs* vfs, int microseconds) {
350   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
351   return wrapped_vfs->xSleep(wrapped_vfs, microseconds);
352 }
353 
GetLastError(sqlite3_vfs * vfs,int e,char * s)354 int GetLastError(sqlite3_vfs* vfs, int e, char* s) {
355   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
356   return wrapped_vfs->xGetLastError(wrapped_vfs, e, s);
357 }
358 
CurrentTimeInt64(sqlite3_vfs * vfs,sqlite3_int64 * now)359 int CurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* now) {
360   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
361   return wrapped_vfs->xCurrentTimeInt64(wrapped_vfs, now);
362 }
363 
364 }  // namespace
365 
VFSWrapper()366 sqlite3_vfs* VFSWrapper() {
367   const char* kVFSName = "VFSWrapper";
368 
369   // Return existing version if already registered.
370   {
371     sqlite3_vfs* vfs = sqlite3_vfs_find(kVFSName);
372     if (vfs)
373       return vfs;
374   }
375 
376   // Get the default VFS on all platforms except Fuchsia.
377   const char* base_vfs_name = nullptr;
378 #if defined(OS_FUCHSIA)
379   base_vfs_name = "unix-none";
380 #endif
381   sqlite3_vfs* wrapped_vfs = sqlite3_vfs_find(base_vfs_name);
382 
383   // Give up if there is no VFS implementation for the current platform.
384   if (!wrapped_vfs) {
385     NOTREACHED();
386     return nullptr;
387   }
388 
389   std::unique_ptr<sqlite3_vfs, std::function<void(sqlite3_vfs*)>> wrapper_vfs(
390       static_cast<sqlite3_vfs*>(sqlite3_malloc(sizeof(sqlite3_vfs))),
391       [](sqlite3_vfs* v) {
392         sqlite3_free(v);
393       });
394   memset(wrapper_vfs.get(), '\0', sizeof(sqlite3_vfs));
395 
396   // VFS implementations should always work with a SQLite that only knows about
397   // earlier versions.
398   constexpr int kSqliteVfsApiVersion = 3;
399   wrapper_vfs->iVersion = kSqliteVfsApiVersion;
400 
401   // All the SQLite VFS implementations used by Chrome should support the
402   // version proxied here.
403   DCHECK_GE(wrapped_vfs->iVersion, kSqliteVfsApiVersion);
404 
405   // Caller of xOpen() allocates this much space.
406   wrapper_vfs->szOsFile = sizeof(VfsFile);
407 
408   wrapper_vfs->mxPathname = wrapped_vfs->mxPathname;
409   wrapper_vfs->pNext = nullptr;  // Field used by SQLite.
410   wrapper_vfs->zName = kVFSName;
411 
412   // Keep a reference to the wrapped vfs for use in methods.
413   wrapper_vfs->pAppData = wrapped_vfs;
414 
415   // VFS methods.
416   wrapper_vfs->xOpen = &Open;
417   wrapper_vfs->xDelete = &Delete;
418   wrapper_vfs->xAccess = &Access;
419   wrapper_vfs->xFullPathname = &FullPathname;
420 
421   // SQLite's dynamic extension loading is disabled in Chrome. Not proxying
422   // these methods lets us ship less logic and provides a tiny bit of extra
423   // security, as we know for sure that SQLite will not dynamically load code.
424   wrapper_vfs->xDlOpen = nullptr;
425   wrapper_vfs->xDlError = nullptr;
426   wrapper_vfs->xDlSym = nullptr;
427   wrapper_vfs->xDlClose = nullptr;
428 
429   wrapper_vfs->xRandomness = &Randomness;
430   wrapper_vfs->xSleep = &Sleep;
431 
432   // |xCurrentTime| is null when SQLite is built with SQLITE_OMIT_DEPRECATED, so
433   // it does not need to be proxied.
434   wrapper_vfs->xCurrentTime = nullptr;
435 
436   wrapper_vfs->xGetLastError = &GetLastError;
437 
438   // The methods above are in version 1 of SQLite's VFS API.
439 
440   DCHECK(wrapped_vfs->xCurrentTimeInt64 != nullptr);
441   wrapper_vfs->xCurrentTimeInt64 = &CurrentTimeInt64;
442 
443   // The methods above are in version 2 of SQLite's VFS API.
444 
445   // The VFS system call interception API is intended for very low-level SQLite
446   // testing and tweaks. Proxying these methods is not necessary because Chrome
447   // does not do very low-level SQLite testing, and the VFS wrapper supports all
448   // the needed tweaks.
449   wrapper_vfs->xSetSystemCall = nullptr;
450   wrapper_vfs->xGetSystemCall = nullptr;
451   wrapper_vfs->xNextSystemCall = nullptr;
452 
453   // The methods above are in version 3 of sqlite_vfs.
454 
455   if (SQLITE_OK == sqlite3_vfs_register(wrapper_vfs.get(), 0)) {
456     ANNOTATE_LEAKING_OBJECT_PTR(wrapper_vfs.get());
457     wrapper_vfs.release();
458   }
459 
460   return sqlite3_vfs_find(kVFSName);
461 }
462 
463 }  // namespace sql
464