Changeset: 09dad7e2d985 for MonetDB URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=09dad7e2d985 Added Files: sql/test/BugTracker-2015/Tests/crash_timestamp_convert.Bug-3816.stable.out.Darwin.src sql/test/BugTracker-2015/Tests/crash_timestamp_convert.Bug-3816.stable.out.FreeBSD sql/test/BugTracker-2017/Tests/fullouterjoinfilter.Bug-6256.sql sql/test/BugTracker-2017/Tests/fullouterjoinfilter.Bug-6256.stable.err sql/test/BugTracker-2017/Tests/fullouterjoinfilter.Bug-6256.stable.out sql/test/BugTracker-2017/Tests/wrong_aggregation_count.Bug-6257.sql sql/test/BugTracker-2017/Tests/wrong_aggregation_count.Bug-6257.stable.err sql/test/BugTracker-2017/Tests/wrong_aggregation_count.Bug-6257.stable.out Modified Files: sql/backends/monet5/sql_execute.c sql/backends/monet5/sql_scenario.h sql/backends/monet5/vaults/fits/fits.c sql/backends/monet5/vaults/netcdf/netcdf.c sql/test/BugTracker-2015/Tests/schema-trigger.Bug-3710.sql sql/test/BugTracker-2015/Tests/schema-trigger.Bug-3710.stable.err sql/test/BugTracker-2015/Tests/schema-trigger.Bug-3710.stable.out sql/test/BugTracker-2016/Tests/convert-function-test.Bug-3460.stable.out sql/test/BugTracker-2016/Tests/convert-function-test.Bug-3460.stable.out.int128 sql/test/BugTracker-2017/Tests/All sql/test/BugTracker-2017/Tests/crash_correlated_subqueries_in_select.Bug-6254.sql sql/test/BugTracker-2017/Tests/crash_correlated_subqueries_in_select.Bug-6254.stable.out Branch: default Log Message:
Merge with Dec2016 branch. diffs (truncated from 1195 to 300 lines): diff --git a/sql/backends/monet5/sql_execute.c b/sql/backends/monet5/sql_execute.c --- a/sql/backends/monet5/sql_execute.c +++ b/sql/backends/monet5/sql_execute.c @@ -352,6 +352,49 @@ SQLrun(Client c, backend *be, mvc *m){ return msg; } +/* + * Escape single quotes and backslashes. This is important to do before calling + * SQLstatementIntern, if we are pasting user provided strings into queries + * passed to the SQLstatementIntern. Otherwise we open ourselves to SQL + * injection attacks. + * + * It returns the input string with all the single quotes(') and the backslashes + * (\) doubled, or NULL, if it could not allocate enough space. + * + * The caller is responsible to free the returned value. + */ +str +SQLescapeString(str s) +{ + str ret = NULL; + char *p, *q; + int len = 0; + + if(!s) { + return NULL; + } + + /* At most we will need 2*strlen(s) + 1 characters */ + len = strlen(s); + ret = (str)GDKmalloc(2*len + 1); + if (!ret) { + return NULL; + } + + for (p = s, q = ret; *p != '\0'; p++, q++) { + *q = *p; + if (*p == '\'') { + *(++q) = '\''; + } + else if (*p == '\\') { + *(++q) = '\\'; + } + } + + *q = '\0'; + return ret; +} + str SQLstatementIntern(Client c, str *expr, str nme, bit execute, bit output, res_table **result) { diff --git a/sql/backends/monet5/sql_scenario.h b/sql/backends/monet5/sql_scenario.h --- a/sql/backends/monet5/sql_scenario.h +++ b/sql/backends/monet5/sql_scenario.h @@ -43,6 +43,7 @@ sql5_export str SQLstatementIntern(Clien sql5_export str SQLcompile(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); sql5_export str SQLinclude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); sql5_export str SQLCacheRemove(Client c, str nme); +sql5_export str SQLescapeString(str s); sql5_export MT_Lock sql_contextLock; #endif /* _SQL_SCENARIO_H_ */ diff --git a/sql/backends/monet5/vaults/fits/fits.c b/sql/backends/monet5/vaults/fits/fits.c --- a/sql/backends/monet5/vaults/fits/fits.c +++ b/sql/backends/monet5/vaults/fits/fits.c @@ -31,8 +31,6 @@ #define FITS_INS_COL "INSERT INTO sys.fits_columns(id, name, type, units, number, table_id) \ VALUES(%d,'%s','%s','%s',%d,%d);" -#define FILE_INS "INSERT INTO sys.fits_files(id, name) VALUES (%d, '%s');" -#define DEL_TABLE "DELETE FROM sys.fitsfiles;" #define ATTACHDIR "call sys.fitsattach('%s');" static void @@ -572,14 +570,25 @@ str FITSdir(Client cntxt, MalBlkPtr mb, s = stmt; while ((ep = readdir(dp)) != NULL && !msg) { - snprintf(fname, BUFSIZ, "%s%s", dir, ep->d_name); + char *filename = SQLescapeString(ep->d_name); + if (!filename) { + msg = createException(MAL, "fits.listdir", MAL_MALLOC_FAIL); + break; + } + + snprintf(fname, BUFSIZ, "%s%s", dir, filename); status = 0; fits_open_file(&fptr, fname, READONLY, &status); if (status == 0) { snprintf(stmt, BUFSIZ, ATTACHDIR, fname); +#ifndef NDEBUG + fprintf(stderr, "Executing:\n%s\n", s); +#endif msg = SQLstatementIntern(cntxt, &s, "fits.listofdir", TRUE, FALSE, NULL); fits_close_file(fptr, &status); } + + GDKfree(filename); } (void)closedir(dp); } else @@ -593,6 +602,7 @@ str FITSdirpat(Client cntxt, MalBlkPtr m str msg = MAL_SUCCEED; str dir = *getArgReference_str(stk, pci, 1); str pat = *getArgReference_str(stk, pci, 2); + char *filename = NULL; fitsfile *fptr; char *s; int status = 0; @@ -601,6 +611,7 @@ str FITSdirpat(Client cntxt, MalBlkPtr m size_t j = 0; (void)mb; + globbuf.gl_offs = 0; snprintf(fulldirectory, BUFSIZ, "%s%s", dir, pat); glob(fulldirectory, GLOB_DOOFFS, NULL, &globbuf); @@ -608,7 +619,7 @@ str FITSdirpat(Client cntxt, MalBlkPtr m /* fprintf(stderr,"#fulldir: %s \nSize: %lu\n",fulldirectory, globbuf.gl_pathc);*/ if (globbuf.gl_pathc == 0) - throw(MAL, "listdir", "Couldn't open the directory or there are no files that match the pattern"); + throw(MAL, "fits.listdirpat", "Couldn't open the directory or there are no files that match the pattern"); for (j = 0; j < globbuf.gl_pathc; j++) { char stmt[BUFSIZ]; @@ -616,12 +627,21 @@ str FITSdirpat(Client cntxt, MalBlkPtr m s = stmt; snprintf(fname, BUFSIZ, "%s", globbuf.gl_pathv[j]); + filename = SQLescapeString(fname); + if (!filename) { + throw(MAL, "fits.listdirpat", MAL_MALLOC_FAIL); + } status = 0; - fits_open_file(&fptr, fname, READONLY, &status); + fits_open_file(&fptr, filename, READONLY, &status); if (status == 0) { - snprintf(stmt, BUFSIZ, ATTACHDIR, fname); + snprintf(stmt, BUFSIZ, ATTACHDIR, filename); + GDKfree(filename); +#ifndef NDEBUG + fprintf(stderr, "Executing:\n%s\n", s); +#endif msg = SQLstatementIntern(cntxt, &s, "fits.listofdirpat", TRUE, FALSE, NULL); fits_close_file(fptr, &status); + break; } } @@ -664,6 +684,7 @@ str FITSattach(Client cntxt, MalBlkPtr m char tname[BUFSIZ], *tname_low = NULL, *s, bname[BUFSIZ], stmt[BUFSIZ]; long tbcol; /* type long used by fits library */ char cname[BUFSIZ], tform[BUFSIZ], tunit[BUFSIZ], tnull[BUFSIZ], tdisp[BUFSIZ]; + char *esc_cname, *esc_tform, *esc_tunit; double tscal, tzero; char xtensionname[BUFSIZ] = "", stilversion[BUFSIZ] = ""; char stilclass[BUFSIZ] = "", tdate[BUFSIZ] = "", orig[BUFSIZ] = "", comm[BUFSIZ] = ""; @@ -807,7 +828,26 @@ str FITSattach(Client cntxt, MalBlkPtr m cid = store_funcs.count_col(tr, col, 1) + 1; for (j = 1; j <= cnum; j++, cid++) { fits_get_acolparms(fptr, j, cname, &tbcol, tunit, tform, &tscal, &tzero, tnull, tdisp, &status); - snprintf(stmt, BUFSIZ, FITS_INS_COL, (int)cid, cname, tform, tunit, j, (int)tid); + /* escape the various strings to avoid SQL injection attacks */ + esc_cname = SQLescapeString(cname); + if (!esc_cname) { + throw(MAL, "fits.attach", MAL_MALLOC_FAIL); + } + esc_tform = SQLescapeString(tform); + if (!esc_tform) { + GDKfree(esc_cname); + throw(MAL, "fits.attach", MAL_MALLOC_FAIL); + } + esc_tunit = SQLescapeString(tunit); + if (!esc_tform) { + GDKfree(esc_tform); + GDKfree(esc_cname); + throw(MAL, "fits.attach", MAL_MALLOC_FAIL); + } + snprintf(stmt, BUFSIZ, FITS_INS_COL, (int)cid, esc_cname, esc_tform, esc_tunit, j, (int)tid); + GDKfree(esc_tunit); + GDKfree(esc_tform); + GDKfree(esc_cname); msg = SQLstatementIntern(cntxt, &s, "fits.attach", TRUE, FALSE, NULL); if (msg != MAL_SUCCEED) { fits_close_file(fptr, &status); diff --git a/sql/backends/monet5/vaults/netcdf/netcdf.c b/sql/backends/monet5/vaults/netcdf/netcdf.c --- a/sql/backends/monet5/vaults/netcdf/netcdf.c +++ b/sql/backends/monet5/vaults/netcdf/netcdf.c @@ -232,13 +232,14 @@ NCDFattach(Client cntxt, MalBlkPtr mb, M size_t dlen, alen; char dname[NC_MAX_NAME+1], vname[NC_MAX_NAME+1], aname[NC_MAX_NAME +1], - abuf[80], *aval; + abuf[80], *aval; char **dims = NULL; nc_type vtype, atype; /* == int */ int retval, avalint; float avalfl; double avaldbl; + str esc_str0, esc_str1; msg = getSQLContext(cntxt, mb, &m, NULL); if (msg) @@ -278,21 +279,34 @@ header: %s", nc_strerror(retval)); col = mvc_bind_column(m, tfiles, "file_id"); fid = store_funcs.count_col(tr, col, 1) + 1; - snprintf(buf, BUFSIZ, INSFILE, (int)fid, fname); + esc_str0 = SQLescapeString(fname); + if (!esc_str0) { + msg = createException(MAL, "netcdf.attach", MAL_MALLOC_FAIL); + goto finish; + } + snprintf(buf, BUFSIZ, INSFILE, (int)fid, esc_str0); + GDKfree(esc_str0); if ( ( msg = SQLstatementIntern(cntxt, &s, "netcdf.attach", TRUE, FALSE, NULL)) - != MAL_SUCCEED ) + != MAL_SUCCEED ) goto finish; /* Read dimensions from NetCDF header and insert a row for each one into netcdf_dims table */ dims = (char **)GDKzalloc(sizeof(char *) * ndims); for (didx = 0; didx < ndims; didx++){ - if ((retval = nc_inq_dim(ncid, didx, dname, &dlen))) + if ((retval = nc_inq_dim(ncid, didx, dname, &dlen)) != 0) return createException(MAL, "netcdf.attach", "Cannot read dimension %d : %s", didx, nc_strerror(retval)); - snprintf(buf, BUFSIZ, INSDIM, didx, (int)fid, dname, (int)dlen); + esc_str0 = SQLescapeString(dname); + if (!esc_str0) { + msg = createException(MAL, "netcdf.attach", MAL_MALLOC_FAIL); + goto finish; + } + + snprintf(buf, BUFSIZ, INSDIM, didx, (int)fid, esc_str0, (int)dlen); + GDKfree(esc_str0); if ( ( msg = SQLstatementIntern(cntxt, &s, "netcdf.attach", TRUE, FALSE, NULL)) - != MAL_SUCCEED ) + != MAL_SUCCEED ) goto finish; dims[didx] = GDKstrdup(dname); @@ -302,24 +316,31 @@ header: %s", nc_strerror(retval)); for (vidx = 0; vidx < nvars; vidx++){ if ( (retval = nc_inq_var(ncid, vidx, vname, &vtype, &vndims, vdims, &vnatts))) return createException(MAL, "netcdf.attach", - "Cannot read variable %d : %s", - vidx, nc_strerror(retval)); + "Cannot read variable %d : %s", + vidx, nc_strerror(retval)); /* Check if this is coordinate variable */ if ( (vndims == 1) && ( strcmp(vname, dims[vdims[0]]) == 0 )) coord_dim_id = vdims[0]; else coord_dim_id = -1; - snprintf(buf, BUFSIZ, INSVAR, vidx, (int)fid, vname, prim_type_name(vtype), vndims, coord_dim_id); + esc_str0 = SQLescapeString(vname); + if (!esc_str0) { + msg = createException(MAL, "netcdf.attach", MAL_MALLOC_FAIL); + goto finish; + } + + snprintf(buf, BUFSIZ, INSVAR, vidx, (int)fid, esc_str0, prim_type_name(vtype), vndims, coord_dim_id); + GDKfree(esc_str0); if ( ( msg = SQLstatementIntern(cntxt, &s, "netcdf.attach", TRUE, FALSE, NULL)) - != MAL_SUCCEED ) + != MAL_SUCCEED ) goto finish; if ( coord_dim_id < 0 ){ for (i = 0; i < vndims; i++){ snprintf(buf, BUFSIZ, INSVARDIM, vidx, vdims[i], (int)fid, i); if ( ( msg = SQLstatementIntern(cntxt, &s, "netcdf.attach", TRUE, FALSE, NULL)) - != MAL_SUCCEED ) + != MAL_SUCCEED ) goto finish; } } @@ -329,62 +350,74 @@ header: %s", nc_strerror(retval)); for (aidx = 0; aidx < vnatts; aidx++){ if ((retval = nc_inq_attname(ncid,vidx,aidx,aname))) return createException(MAL, "netcdf.attach", - "Cannot read attribute %d of variable %d: %s", - aidx, vidx, nc_strerror(retval)); + "Cannot read attribute %d of variable %d: %s", + aidx, vidx, nc_strerror(retval)); - if ((retval = nc_inq_att(ncid,vidx,aname,&atype,&alen))) + if ((retval = nc_inq_att(ncid,vidx,aname,&atype,&alen))) return createException(MAL, "netcdf.attach", - "Cannot read attribute %s type and length: %s", - aname, nc_strerror(retval)); + "Cannot read attribute %s type and length: %s", + aname, nc_strerror(retval)); _______________________________________________ checkin-list mailing list checkin-list@monetdb.org https://www.monetdb.org/mailman/listinfo/checkin-list