diff --git a/.gitignore b/.gitignore index 895948373..953fae3eb 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ generated/resources/fonts/noto generated/resources/fonts/sil tags cscope.* +ID # Editor and file browser turds: *~ diff --git a/Makefile b/Makefile index 3160a56d9..86ea80135 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,10 @@ include Makethird # Do not specify CFLAGS or LIBS on the make invocation line - specify # XCFLAGS or XLIBS instead. Make ignores any lines in the makefile that # set a variable that was set on the command line. -CFLAGS += $(XCFLAGS) -Iinclude +CFLAGS += $(XCFLAGS) -Iinclude -O3 -march=native -fPIC -U_FORTIFY_SOURCE \ + -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection \ + -fcf-protection=full --param=ssp-buffer-size=4 -fno-omit-frame-pointer -gdwarf-5 \ + -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 LIBS += $(XLIBS) -lm ifneq ($(threading),no) @@ -60,7 +63,7 @@ AR_CMD = $(QUIET_AR) $(MKTGTDIR) ; $(AR) cr $@ $^ ifdef RANLIB RANLIB_CMD = $(QUIET_RANLIB) $(RANLIB) $@ endif -LINK_CMD = $(QUIET_LINK) $(MKTGTDIR) ; $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) +LINK_CMD = $(QUIET_LINK) $(MKTGTDIR) ; $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -pie -Wl,-z,relro -Wl,-z,now TAGS_CMD = $(QUIET_TAGS) ctags -R --c-kinds=+p --exclude=platform/python --exclude=platform/c++ WINDRES_CMD = $(QUIET_WINDRES) $(MKTGTDIR) ; $(WINDRES) $< $@ OBJCOPY_CMD = $(QUIET_OBJCOPY) $(MKTGTDIR) ; $(LD) -r -b binary -z noexecstack -o $@ $< @@ -68,7 +71,7 @@ GENDEF_CMD = $(QUIET_GENDEF) gendef - $< > $@ DLLTOOL_CMD = $(QUIET_DLLTOOL) dlltool -d $< -D $(notdir $(^:%.def=%.dll)) -l $@ ifeq ($(shared),yes) -LINK_CMD = $(QUIET_LINK) $(MKTGTDIR) ; $(CC) $(LDFLAGS) -o $@ \ +LINK_CMD = $(QUIET_LINK) $(MKTGTDIR) ; $(CC) $(LDFLAGS) -o $@ -pie -Wl,-z,relro -Wl,-z,now \ $(filter-out %.$(SO),$^) \ $(sort $(patsubst %,-L%,$(dir $(filter %.$(SO),$^)))) \ $(patsubst lib%.$(SO),-l%,$(notdir $(filter %.$(SO),$^))) \ @@ -304,7 +307,7 @@ ifeq ($(HAVE_GLUT),yes) MUVIEW_GLUT_OBJ := $(MUVIEW_GLUT_SRC:%.c=$(OUT)/%.o) MUVIEW_GLUT_EXE := $(OUT)/mupdf-gl$(EXE) $(MUVIEW_GLUT_EXE) : $(MUVIEW_GLUT_OBJ) $(MUPDF_LIB) $(THIRD_LIB) $(THIRD_GLUT_LIB) $(PKCS7_LIB) - $(LINK_CMD) $(THIRD_LIBS) $(LIBCRYPTO_LIBS) $(WIN32_LDFLAGS) $(THIRD_GLUT_LIBS) + $(LINK_CMD) $(THIRD_LIBS) $(LIBCRYPTO_LIBS) $(WIN32_LDFLAGS) $(THIRD_GLUT_LIBS) -lsystemd VIEW_APPS += $(MUVIEW_GLUT_EXE) endif @@ -468,7 +471,7 @@ cscope.out: cscope.files all: libs apps clean: - rm -rf $(OUT) + rm -rf -- $(OUT) nuke: rm -rf build/* rm -rf generated/resources/fonts/droid diff --git a/Makerules b/Makerules index 8b5d3e9ce..d0a6067ec 100644 --- a/Makerules +++ b/Makerules @@ -72,19 +72,19 @@ ifeq ($(build),debug) LDFLAGS += -g else ifeq ($(build),release) CFLAGS += -pipe -O2 -DNDEBUG -fomit-frame-pointer - LDFLAGS += $(LDREMOVEUNREACH) -Wl,-s + LDFLAGS += $(LDREMOVEUNREACH) else ifeq ($(build),small) CFLAGS += -pipe -Os -DNDEBUG -fomit-frame-pointer - LDFLAGS += $(LDREMOVEUNREACH) -Wl,-s + LDFLAGS += $(LDREMOVEUNREACH) else ifeq ($(build),valgrind) CFLAGS += -pipe -O2 -DNDEBUG -DPACIFY_VALGRIND -fno-omit-frame-pointer - LDFLAGS += $(LDREMOVEUNREACH) -Wl,-s + LDFLAGS += $(LDREMOVEUNREACH) else ifeq ($(build),sanitize) - CFLAGS += -pipe -g -fno-omit-frame-pointer $(SANITIZE_FLAGS) - LDFLAGS += -g $(SANITIZE_FLAGS) + CFLAGS += -pipe -fno-omit-frame-pointer $(SANITIZE_FLAGS) + LDFLAGS += $(SANITIZE_FLAGS) else ifeq ($(build),sanitize-release) CFLAGS += -pipe -O2 -DNDEBUG -fno-omit-frame-pointer $(SANITIZE_FLAGS) - LDFLAGS += $(LDREMOVEUNREACH) -Wl,-s $(SANITIZE_FLAGS) + LDFLAGS += $(LDREMOVEUNREACH) $(SANITIZE_FLAGS) else ifeq ($(build),profile) CFLAGS += -pipe -O2 -DNDEBUG -pg LDFLAGS += -pg @@ -93,7 +93,7 @@ else ifeq ($(build),coverage) LIBS += -lgcov else ifeq ($(build),native) CFLAGS += -pipe -O2 -DNDEBUG -fomit-frame-pointer -march=native - LDFLAGS += $(LDREMOVEUNREACH) -Wl,-s + LDFLAGS += $(LDREMOVEUNREACH) else ifeq ($(build),memento) CFLAGS += -pipe -g -DMEMENTO -DMEMENTO_MUPDF_HACKS LDFLAGS += -g -rdynamic diff --git a/Makethird b/Makethird index cbf12486b..059b269d4 100644 --- a/Makethird +++ b/Makethird @@ -11,7 +11,7 @@ ifeq ($(USE_SYSTEM_LIBS),yes) USE_SYSTEM_MUJS := no # not available USE_SYSTEM_OPENJPEG := yes USE_SYSTEM_ZLIB := yes - USE_SYSTEM_GLUT := yes + USE_SYSTEM_GLUT := no USE_SYSTEM_CURL := yes USE_SYSTEM_LEPTONICA := yes USE_SYSTEM_TESSERACT := yes diff --git a/include/mupdf/fitz/system.h b/include/mupdf/fitz/system.h index 6290c240a..2bdd374e5 100644 --- a/include/mupdf/fitz/system.h +++ b/include/mupdf/fitz/system.h @@ -60,7 +60,7 @@ typedef unsigned __int64 uint64_t; #include "mupdf/memento.h" #include "mupdf/fitz/track-usage.h" -#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define nelem(arr) (sizeof(arr) / sizeof((arr)[0])) #define FZ_PI 3.14159265f #define FZ_RADIAN 57.2957795f diff --git a/include/mupdf/siphash.h b/include/mupdf/siphash.h new file mode 100644 index 000000000..c193220be --- /dev/null +++ b/include/mupdf/siphash.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +typedef struct { + uint64_t key[2]; +} siphash_key_t; + +uint64_t siphash(const siphash_key_t *key, const unsigned char *m, size_t len); diff --git a/platform/gl/gl-file.c b/platform/gl/gl-file.c index 40cec0d2e..dc12d00db 100644 --- a/platform/gl/gl-file.c +++ b/platform/gl/gl-file.c @@ -100,7 +100,8 @@ static void load_dir(const char *path) char buf[PATH_MAX]; int i; - fz_realpath(path, fc.curdir); + if (fz_realpath(path, fc.curdir) == NULL) + return; if (!fz_is_directory(ctx, path)) return; @@ -217,7 +218,8 @@ static void load_dir(const char *path) DIR *dir; struct dirent *dp; - fz_realpath(path, fc.curdir); + if (fz_realpath(path, fc.curdir) == NULL) + return; if (!fz_is_directory(ctx, fc.curdir)) return; diff --git a/platform/gl/gl-main.c b/platform/gl/gl-main.c index 076cad269..e2c97c329 100644 --- a/platform/gl/gl-main.c +++ b/platform/gl/gl-main.c @@ -22,10 +22,21 @@ #include "gl-app.h" +#include #include #include #include #include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#define stat _stat +#endif + #include #ifndef _WIN32 #include @@ -36,6 +47,7 @@ #include "mupdf/helpers/pkcs7-openssl.h" #include "mujs.h" +#include "mupdf/siphash.h" #ifndef PATH_MAX #define PATH_MAX 4096 @@ -92,6 +104,7 @@ static void open_browser(const char *uri) char buf_base[PATH_MAX]; char buf_cwd[PATH_MAX]; fz_dirname(buf_base, filename, sizeof buf_base); + if (getcwd(buf_cwd, sizeof buf_cwd)) { fz_snprintf(buf, sizeof buf, "file://%s/%s/%s", buf_cwd, buf_base, uri+7); @@ -124,8 +137,8 @@ static void open_browser(const char *uri) argv[2] = NULL; err = posix_spawn(&pid, browser, NULL, NULL, argv, environ); if (err) - fz_warn(ctx, "cannot spawn browser '%s': %s", browser, strerror(err)); - + fz_warn(ctx, "cannot spawn browser '%s': %s", + browser, strerror(err)); #endif } @@ -258,14 +271,16 @@ static char *get_history_filename(void) char *home = getenv("MUPDF_HISTORY"); if (home) return home; - home = getenv("XDG_CACHE_HOME"); - if (!home) - home = getenv("HOME"); - if (!home) - home = getenv("USERPROFILE"); - if (!home) - home = "/tmp"; - fz_snprintf(history_path, sizeof history_path, "%s/.mupdf.history", home); + home = getenv("HOME"); + if (!home) return NULL; + fz_snprintf(history_path, sizeof history_path, "%s/.config/mupdf", home); + if ((mkdir(history_path, 0700) == -1) && (errno != EEXIST)) { + fz_snprintf(history_path, sizeof history_path, + "%s/.mupdf-history.json", home); + } else { + fz_snprintf(history_path, sizeof history_path, + "%s/.config/mupdf/mupdf-history.json", home); + } fz_cleanname(history_path); once = 1; } @@ -281,7 +296,7 @@ static int read_history_file_as_json(js_State *J) fz_var(buf); history_file = get_history_filename(); - if (strlen(history_file) == 0) + if (!history_file || !history_file[0]) return 0; if (fz_file_exists(ctx, history_file)) @@ -314,6 +329,36 @@ static int read_history_file_as_json(js_State *J) return 1; } +/* encode uint64_t as base64, requires 14 byte buffer at [out] */ +static inline void base32_encode_u64(char *out, uint64_t in) +{ + size_t i = 13; + + while (i--) { + *out++ = "0123456789bcdfghjklmnpqrstuvwxyz"[in & 31]; + in >>= 5; + } + *out = 0; +} + +#define MUPDF_HIST_ID SD_ID128_MAKE(79,b9,ce,cd,94,7b,7e,d2,ea,25,98,18,d7,83,a0,a7) +/* To preserve privacy, convert absolute filename to 64 bit hash encoded as base32 string */ +static void absname_to_digest(char *abs, char *dig) +{ + static siphash_key_t sipkey; + static bool sipinit = 0; + sd_id128_t hist_id; + + if (!sipinit) { + sipinit = 1; + sd_id128_get_machine_app_specific(MUPDF_HIST_ID, &hist_id); + sipkey.key[0] = hist_id.qwords[0]; + sipkey.key[1] = hist_id.qwords[1]; + } + base32_encode_u64(dig, siphash(&sipkey, (unsigned char*)abs, strlen(abs))); + //fprintf(stderr, "absname_to_digest [%s] [%s]\n", abs, dig); +} + static fz_location try_location(js_State *J) { fz_location loc; @@ -348,18 +393,20 @@ static void push_location(js_State *J, fz_location loc) static void load_history(void) { js_State *J; + char digest[14]; char absname[PATH_MAX]; int i, n; if (!fz_realpath(filename, absname)) return; + absname_to_digest(absname, digest); J = js_newstate(NULL, NULL, 0); if (!read_history_file_as_json(J)) return; - if (js_hasproperty(J, -1, absname)) + if (js_hasproperty(J, -1, digest)) { if (js_hasproperty(J, -1, "current")) { @@ -420,6 +467,7 @@ static void save_history(void) { js_State *J; char absname[PATH_MAX]; + char digest[14]; fz_output *out = NULL; const char *json; int i; @@ -431,6 +479,7 @@ static void save_history(void) if (!fz_realpath(filename, absname)) return; + absname_to_digest(absname, digest); J = js_newstate(NULL, NULL, 0); @@ -466,7 +515,7 @@ static void save_history(void) } js_setproperty(J, -2, "marks"); } - js_setproperty(J, -2, absname); + js_setproperty(J, -2, digest); js_getglobal(J, "JSON"); js_getproperty(J, -1, "stringify"); @@ -481,7 +530,7 @@ static void save_history(void) fz_try(ctx) { const char *history_file = get_history_filename(); - if (strlen(history_file) > 0) { + if (history_file && history_file[0]) { out = fz_new_output_with_path(ctx, history_file, 0); fz_write_string(ctx, out, json); fz_write_byte(ctx, out, '\n'); @@ -496,10 +545,23 @@ static void save_history(void) js_freestate(J); } +/* To preserve privacy, convert accel path to 64 bit hash encoded as base32 string */ +#define MUPDF_ACCEL_ID SD_ID128_MAKE(59,b6,b0,85,22,20,08,a9,89,6c,ec,fc,88,0e,e8,b5) static int convert_to_accel_path(char outname[], char *absname, size_t len) { char *tmpdir; char *s; + static siphash_key_t sipkey; + static bool sipinit = 0; + sd_id128_t accel_id; + char sip_accelid[14]; + + if (!sipinit) { + sipinit = 1; + sd_id128_get_machine_app_specific(MUPDF_ACCEL_ID, &accel_id); + sipkey.key[0] = accel_id.qwords[0]; + sipkey.key[1] = accel_id.qwords[1]; + } tmpdir = getenv("TEMP"); if (!tmpdir) @@ -511,15 +573,11 @@ static int convert_to_accel_path(char outname[], char *absname, size_t len) if (absname[0] == '/' || absname[0] == '\\') ++absname; - s = absname; - while (*s) { - if (*s == '/' || *s == '\\' || *s == ':') - *s = '%'; - ++s; - } + base32_encode_u64(sip_accelid, siphash(&sipkey, (const unsigned char*)s, strlen((const char*)s))); + //fprintf(stderr, "accel_to_digest [%s] [%s]\n", s, sip_accelid); - if (fz_snprintf(outname, len, "%s/%s.accel", tmpdir, absname) >= len) + if (fz_snprintf(outname, len, "%s/.mupdf-%s.accel", tmpdir, sip_accelid) >= len) return 0; return 1; } @@ -812,19 +870,19 @@ void update_title(void) if (n > 50) { if (nc == 1) - sprintf(buf, "...%s%s - %d/%d", title + n - 50, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); + snprintf(buf, sizeof(buf), "...%s%s - %d/%d", title + n - 50, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); else - sprintf(buf, "...%s%s - %d/%d - %d/%d", title + n - 50, extra, + snprintf(buf, sizeof(buf), "...%s%s - %d/%d - %d/%d", title + n - 50, extra, currentpage.chapter + 1, nc, currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); } else { if (nc == 1) - sprintf(buf, "%s%s - %d/%d", title, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); + snprintf(buf, sizeof(buf), "%s%s - %d/%d", title, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); else - sprintf(buf, "%s%s - %d/%d - %d/%d", title, extra, + snprintf(buf, sizeof(buf), "%s%s - %d/%d - %d/%d", title, extra, currentpage.chapter + 1, nc, currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); } diff --git a/source/fitz/siphash.c b/source/fitz/siphash.c new file mode 100644 index 000000000..968979e2d --- /dev/null +++ b/source/fitz/siphash.c @@ -0,0 +1,86 @@ +#include + +#include "mupdf/siphash.h" + +#if defined(_MSC_VER) +#include + +#define INLINE __forceinline +#define NOINLINE __declspec(noinline) +#define ROTL64(a,b) _rotl64(a,b) +#define MM16 __declspec(align(16)) + + typedef unsigned int uint32_t; + typedef unsigned __int64 uint64_t; + +#if (_MSC_VER >= 1500) + #define __SSSE3__ +#endif +#if (_MSC_VER > 1200) || defined(_mm_free) + #define __SSE2__ +#endif +#else + #include + #include + + #define INLINE __attribute__((always_inline)) + #define NOINLINE __attribute__((noinline)) + #define ROTL64(a,b) (((a)<<(b))|((a)>>(64-b))) + #define MM16 __attribute__((aligned(16))) +#endif + +uint64_t siphash(const siphash_key_t *key, const unsigned char *m, size_t len) { + uint64_t v0, v1, v2, v3; + uint64_t mi, k0, k1; + uint64_t last7; + size_t i, blocks; + + k0 = key->key[0]; + k1 = key->key[1]; + v0 = k0 ^ 0x736f6d6570736575ull; + v1 = k1 ^ 0x646f72616e646f6dull; + v2 = k0 ^ 0x6c7967656e657261ull; + v3 = k1 ^ 0x7465646279746573ull; + + last7 = (uint64_t)(len & 0xff) << 56; + +#define sipcompress() \ + v0 += v1; v2 += v3; \ + v1 = ROTL64(v1,13); v3 = ROTL64(v3,16); \ + v1 ^= v0; v3 ^= v2; \ + v0 = ROTL64(v0,32); \ + v2 += v1; v0 += v3; \ + v1 = ROTL64(v1,17); v3 = ROTL64(v3,21); \ + v1 ^= v2; v3 ^= v0; \ + v2 = ROTL64(v2,32); + + for (i = 0, blocks = (len & ~7); i < blocks; i += 8) { + memcpy(&mi, m + i, sizeof(mi)); + v3 ^= mi; + sipcompress() + sipcompress() + v0 ^= mi; + } + + switch (len - blocks) { + case 7: last7 |= (uint64_t)m[i + 6] << 48; + case 6: last7 |= (uint64_t)m[i + 5] << 40; + case 5: last7 |= (uint64_t)m[i + 4] << 32; + case 4: last7 |= (uint64_t)m[i + 3] << 24; + case 3: last7 |= (uint64_t)m[i + 2] << 16; + case 2: last7 |= (uint64_t)m[i + 1] << 8; + case 1: last7 |= (uint64_t)m[i + 0] ; + case 0: + default:; + }; + v3 ^= last7; + sipcompress() + sipcompress() + v0 ^= last7; + v2 ^= 0xff; + sipcompress() + sipcompress() + sipcompress() + sipcompress() + return v0 ^ v1 ^ v2 ^ v3; +} diff --git a/source/fitz/unzip.c b/source/fitz/unzip.c index 78cf989d7..a907dc0de 100644 --- a/source/fitz/unzip.c +++ b/source/fitz/unzip.c @@ -302,7 +302,7 @@ static fz_stream *open_zip_entry(fz_context *ctx, fz_archive *arch, const char * ent = lookup_zip_entry(ctx, zip, name); if (!ent) - fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named zip archive entry"); + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named zip archive entry \"%s\"", name); method = read_zip_entry_header(ctx, zip, ent); if (method == 0) @@ -328,7 +328,7 @@ static fz_buffer *read_zip_entry(fz_context *ctx, fz_archive *arch, const char * ent = lookup_zip_entry(ctx, zip, name); if (!ent) - fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named zip archive entry"); + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named zip archive entry \"%s\"", name); method = read_zip_entry_header(ctx, zip, ent); ubuf = fz_new_buffer(ctx, ent->usize + 1); /* +1 because many callers will add a terminating zero */ diff --git a/source/html/epub-doc.c b/source/html/epub-doc.c index a262e6fb1..40eff36ec 100644 --- a/source/html/epub-doc.c +++ b/source/html/epub-doc.c @@ -692,10 +692,12 @@ epub_parse_header(fz_context *ctx, epub_document *doc) epub_chapter **tailp; int i; +#if 0 if (fz_has_archive_entry(ctx, zip, "META-INF/rights.xml")) fz_throw(ctx, FZ_ERROR_GENERIC, "EPUB is locked by DRM"); if (fz_has_archive_entry(ctx, zip, "META-INF/encryption.xml")) fz_throw(ctx, FZ_ERROR_GENERIC, "EPUB is locked by DRM"); +#endif fz_var(buf); fz_var(container_xml); diff --git a/source/html/html-parse.c b/source/html/html-parse.c index 3ceb17e7e..a3a93ff0c 100644 --- a/source/html/html-parse.c +++ b/source/html/html-parse.c @@ -715,7 +715,7 @@ static void insert_inline_box(fz_context *ctx, fz_html_box *box, fz_html_box *to } else { - while (top->type != BOX_BLOCK && top->type != BOX_TABLE_CELL) + while (top && top->type != BOX_BLOCK && top->type != BOX_TABLE_CELL) { if (top->up == NULL) { @@ -726,13 +726,14 @@ static void insert_inline_box(fz_context *ctx, fz_html_box *box, fz_html_box *to } /* Here 'next' actually means 'last of my children' */ - if (top->next && top->next->type == BOX_FLOW) + if (top && top->next && top->next->type == BOX_FLOW) { insert_box(ctx, box, BOX_INLINE, top->next); } else { fz_css_style style; + if (!top) return; fz_html_box *flow = new_short_box(ctx, g->pool, markup_dir); flow->is_first_flow = !top->next; fz_default_css_style(ctx, &style);