From 75a1d7d89120443711212dc9a03f25b37c5e491b Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Sun, 30 Aug 2009 20:29:15 +0200 Subject: [PATCH] Fix bug #1355 Director crashes with double free in Accurate SQL query --- bacula/src/cats/bvfs.c | 37 +++++++++++++++++------------------- bacula/src/cats/bvfs.h | 8 ++++---- bacula/src/cats/cats.h | 10 ++++++++++ bacula/src/cats/protos.h | 7 ++++--- bacula/src/cats/sql.c | 16 +++++++++++++++- bacula/src/cats/sql_get.c | 26 ++++++------------------- bacula/src/dird/backup.c | 28 +++++++++------------------ bacula/src/dird/ua_dotcmds.c | 9 +++++---- bacula/src/dird/ua_output.c | 2 +- bacula/src/dird/ua_restore.c | 6 ++++-- bacula/src/dird/vbackup.c | 24 +++++++++++------------ bacula/src/lib/edit.c | 18 +++++++++--------- bacula/technotes | 2 ++ 13 files changed, 97 insertions(+), 96 deletions(-) diff --git a/bacula/src/cats/bvfs.c b/bacula/src/cats/bvfs.c index c27ab50fce..b4feb80209 100644 --- a/bacula/src/cats/bvfs.c +++ b/bacula/src/cats/bvfs.c @@ -59,9 +59,8 @@ Bvfs::Bvfs(JCR *j, B_DB *mdb) { jcr->inc_use_count(); db = mdb; /* need to inc ref count */ prev_dir = get_pool_memory(PM_NAME); - jobids = get_pool_memory(PM_NAME); pattern = get_pool_memory(PM_NAME); - *prev_dir = *pattern = *jobids = 0; + *prev_dir = *pattern = 0; dir_filenameid = pwd_id = offset = 0; see_copies = see_all_version = false; limit = 1000; @@ -71,7 +70,6 @@ Bvfs::Bvfs(JCR *j, B_DB *mdb) { } Bvfs::~Bvfs() { - free_pool_memory(jobids); free_pool_memory(pattern); free_pool_memory(prev_dir); free_attr(attr); @@ -363,6 +361,8 @@ DBId_t Bvfs::get_dir_filenameid() void bvfs_update_cache(JCR *jcr, B_DB *mdb) { uint32_t nb=0; + db_list_ctx jobids; + db_lock(mdb); db_start_transaction(jcr, mdb); @@ -406,18 +406,15 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb) } - POOLMEM *jobids = get_pool_memory(PM_NAME); - *jobids = 0; - Mmsg(mdb->cmd, "SELECT JobId from Job " "WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid) " "AND Type IN ('B') AND JobStatus IN ('T', 'f', 'A') " "ORDER BY JobId"); - db_sql_query(mdb, mdb->cmd, db_get_int_handler, jobids); + db_sql_query(mdb, mdb->cmd, db_list_handler, &jobids); - bvfs_update_path_hierarchy_cache(jcr, mdb, jobids); + bvfs_update_path_hierarchy_cache(jcr, mdb, &jobids); db_end_transaction(jcr, mdb); db_start_transaction(jcr, mdb); @@ -438,28 +435,28 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb) Dmsg1(dbglevel, "Affected row(s) = %d\n", nb); db_end_transaction(jcr, mdb); - free_pool_memory(jobids); } /* * Update the bvfs cache for given jobids (1,2,3,4) */ void -bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, char *jobids) +bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, db_list_ctx *jobids) { pathid_cache ppathid_cache; JobId_t JobId; char *p; - for (p=jobids; ; ) { + for (p=jobids->list; ; ) { int stat = get_next_jobid_from_list(&p, &JobId); + Dmsg1(dbglevel, "Updating cache for %lld\n", (uint64_t)JobId); if (stat < 0) { return; } if (stat == 0) { break; } - Dmsg1(dbglevel, "Updating cache for %lld\n", (uint64_t) JobId); + Dmsg1(dbglevel, "Updating cache for %lld\n", (uint64_t)JobId); update_path_hierarchy_cache(jcr, mdb, ppathid_cache, JobId); } } @@ -469,7 +466,7 @@ bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, char *jobids) */ void Bvfs::update_cache() { - bvfs_update_path_hierarchy_cache(jcr, db, jobids); + bvfs_update_path_hierarchy_cache(jcr, db, &jobids); } /* Change the current directory, returns true if the path exists */ @@ -550,7 +547,7 @@ void Bvfs::ls_special_dirs() { Dmsg1(dbglevel, "ls_special_dirs(%lld)\n", (uint64_t)pwd_id); char ed1[50], ed2[50]; - if (!*jobids) { + if (jobids.count == 0) { return; } if (!dir_filenameid) { @@ -579,7 +576,7 @@ void Bvfs::ls_special_dirs() "AND File1.JobId IN (%s)) AS listfile1 " "ON (tmp.PathId = listfile1.PathId) " "ORDER BY tmp.Path, JobId DESC ", - query.c_str(), edit_uint64(dir_filenameid, ed2), jobids); + query.c_str(), edit_uint64(dir_filenameid, ed2), jobids.list); Dmsg1(dbglevel_sql, "q=%s\n", query2.c_str()); db_sql_query(db, query2.c_str(), path_handler, this); @@ -590,7 +587,7 @@ bool Bvfs::ls_dirs() { Dmsg1(dbglevel, "ls_dirs(%lld)\n", (uint64_t)pwd_id); char ed1[50], ed2[50]; - if (!*jobids) { + if (jobids.count == 0) { return false; } @@ -640,10 +637,10 @@ bool Bvfs::ls_dirs() "ON (listpath1.PathId = listfile1.PathId) " ") AS A ORDER BY 2,3 DESC LIMIT %d OFFSET %d", edit_uint64(pwd_id, ed1), - jobids, + jobids.list, filter.c_str(), edit_uint64(dir_filenameid, ed2), - jobids, + jobids.list, limit, offset); Dmsg1(dbglevel_sql, "q=%s\n", query.c_str()); @@ -661,7 +658,7 @@ bool Bvfs::ls_files() { Dmsg1(dbglevel, "ls_files(%lld)\n", (uint64_t)pwd_id); char ed1[50]; - if (!*jobids) { + if (jobids.count == 0) { return false; } @@ -690,7 +687,7 @@ bool Bvfs::ls_files() ") AS listfiles " "WHERE File.FileId = listfiles.id", edit_uint64(pwd_id, ed1), - jobids, + jobids.list, filter.c_str(), limit, offset); diff --git a/bacula/src/cats/bvfs.h b/bacula/src/cats/bvfs.h index 9abe7c485e..72575abaf6 100644 --- a/bacula/src/cats/bvfs.h +++ b/bacula/src/cats/bvfs.h @@ -71,11 +71,11 @@ public: virtual ~Bvfs(); void set_jobid(JobId_t id) { - Mmsg(jobids, "%lld", (uint64_t)id); + Mmsg(jobids.list, "%lld", (uint64_t)id); } void set_jobids(char *ids) { - pm_strcpy(jobids, ids); + pm_strcpy(jobids.list, ids); } void set_limit(uint32_t max) { @@ -154,7 +154,7 @@ public: private: JCR *jcr; B_DB *db; - POOLMEM *jobids; + db_list_ctx jobids; uint32_t limit; uint32_t offset; uint32_t nb_record; /* number of records of the last query */ @@ -173,7 +173,7 @@ private: void *user_data; }; -void bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, char *jobids); +void bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, db_list_ctx *jobids); void bvfs_update_cache(JCR *jcr, B_DB *mdb); char *bvfs_parent_dir(char *path); diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index a2913dddff..4613b67b5c 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -1044,6 +1044,16 @@ struct db_int64_ctx { int count; /* number of values seen */ }; +/* Call back context for getting a list of comma separated strings from the database */ +class db_list_ctx { +public: + POOLMEM *list; /* list */ + int count; /* number of values seen */ + + db_list_ctx() { list = get_pool_memory(PM_FNAME); *list = 0; count = 0; } + ~db_list_ctx() { free_pool_memory(list); list = NULL; } +}; + #include "protos.h" #include "jcr.h" diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index 5214c8a05e..544ef803d5 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2008 Free Software Foundation Europe e.V. + Copyright (C) 2000-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -57,6 +57,7 @@ bool db_sql_query(B_DB *mdb, const char *cmd, DB_RESULT_HANDLER *result_handler, void db_start_transaction(JCR *jcr, B_DB *mdb); void db_end_transaction(JCR *jcr, B_DB *mdb); int db_int64_handler(void *ctx, int num_fields, char **row); +int db_list_handler(void *ctx, int num_fields, char **row); void db_thread_cleanup(); void _dbg_print_db(JCR *jcr, FILE *fp); int db_int_handler(void *ctx, int num_fields, char **row); @@ -109,8 +110,8 @@ int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr); int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr); bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids); bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, DB_RESULT_HANDLER *result_handler, void *ctx); -bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *jobids); -int db_get_int_handler(void *ctx, int num_fields, char **row); +bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, JOB_DBR *jr, db_list_ctx *jobids); +int db_get_int_handler(void *list, int num_fields, char **row); /* sql_list.c */ diff --git a/bacula/src/cats/sql.c b/bacula/src/cats/sql.c index 22eff8ef79..49e9762010 100644 --- a/bacula/src/cats/sql.c +++ b/bacula/src/cats/sql.c @@ -144,7 +144,21 @@ int db_int64_handler(void *ctx, int num_fields, char **row) return 0; } - +/* + * Use to build a comma separated list of values from a query. "10,20,30" + */ +int db_list_handler(void *ctx, int num_fields, char **row) +{ + db_list_ctx *lctx = (db_list_ctx *)ctx; + if (num_fields == 1 && row[0]) { + if (lctx->list[0]) { + pm_strcat(lctx->list, ","); + } + pm_strcat(lctx->list, row[0]); + lctx->count++; + } + return 0; +} /* NOTE!!! The following routines expect that the * calling subroutine sets and clears the mutex diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index 6098bdfa32..e83359e3bb 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2008 Free Software Foundation Europe e.V. + Copyright (C) 2000-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -1099,7 +1099,7 @@ bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, * TODO: look and merge from ua_restore.c */ bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, - JOB_DBR *jr, POOLMEM *jobids) + JOB_DBR *jr, db_list_ctx *jobids) { bool ret=false; char clientid[50], jobid[50], filesetid[50]; @@ -1110,7 +1110,8 @@ bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL); bstrutime(date, sizeof(date), StartTime + 1); - jobids[0]='\0'; + jobids->list[0] = 0; + jobids->count = 0; /* First, find the last good Full backup for this job/client/fileset */ Mmsg(query, @@ -1176,8 +1177,8 @@ bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, /* build a jobid list ie: 1,2,3,4 */ Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid); - db_sql_query(mdb, query.c_str(), db_get_int_handler, jobids); - Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids); + db_sql_query(mdb, query.c_str(), db_list_handler, jobids); + Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list); ret = true; bail_out: @@ -1187,19 +1188,4 @@ bail_out: return ret; } -/* - * Use to build a string of int list from a query. "10,20,30" - */ -int db_get_int_handler(void *ctx, int num_fields, char **row) -{ - POOLMEM *ret = (POOLMEM *)ctx; - if (num_fields == 1 && row[0]) { - if (ret[0]) { - pm_strcat(ret, ","); - } - pm_strcat(ret, row[0]); - } - return 0; -} - #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */ diff --git a/bacula/src/dird/backup.c b/bacula/src/dird/backup.c index bcc02c9120..80f11ab108 100644 --- a/bacula/src/dird/backup.c +++ b/bacula/src/dird/backup.c @@ -131,18 +131,15 @@ static int accurate_list_handler(void *ctx, int num_fields, char **row) bool send_accurate_current_files(JCR *jcr) { POOL_MEM buf; - POOLMEM *jobids; - POOLMEM *nb; + db_list_ctx jobids; + db_list_ctx nb; if (!jcr->accurate || job_canceled(jcr) || jcr->get_JobLevel()==L_FULL) { return true; } - jobids = get_pool_memory(PM_FNAME); + db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids); - db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids); - - if (*jobids == 0) { - free_pool_memory(jobids); + if (jobids.count == 0) { Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n")); return false; } @@ -150,27 +147,20 @@ bool send_accurate_current_files(JCR *jcr) Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n")); } /* to be able to allocate the right size for htable */ - nb = get_pool_memory(PM_FNAME); - *nb = 0; /* clear buffer */ - Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)",jobids); - db_sql_query(jcr->db, buf.c_str(), db_get_int_handler, nb); - Dmsg2(200, "jobids=%s nb=%s\n", jobids, nb); - jcr->file_bsock->fsend("accurate files=%s\n", nb); + Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list); + db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb); + Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list); + jcr->file_bsock->fsend("accurate files=%s\n", nb.list); if (!db_open_batch_connexion(jcr, jcr->db)) { - free_pool_memory(jobids); - free_pool_memory(nb); Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion"); return false; } - db_get_file_list(jcr, jcr->db_batch, jobids, accurate_list_handler, (void *)jcr); + db_get_file_list(jcr, jcr->db_batch, jobids.list, accurate_list_handler, (void *)jcr); /* TODO: close the batch connexion ? (can be used very soon) */ - free_pool_memory(jobids); - free_pool_memory(nb); - jcr->file_bsock->signal(BNET_EOD); return true; diff --git a/bacula/src/dird/ua_dotcmds.c b/bacula/src/dird/ua_dotcmds.c index 363d12fd8f..c444efa0a3 100644 --- a/bacula/src/dird/ua_dotcmds.c +++ b/bacula/src/dird/ua_dotcmds.c @@ -156,15 +156,16 @@ bool do_a_dot_command(UAContext *ua) static bool dot_update(UAContext *ua, const char *cmd) { + if (!open_client_db(ua)) { return 1; } int pos = find_arg_with_value(ua, "jobid"); - if (pos != -1) { /* find jobid arg */ - if (is_a_number_list(ua->argv[pos])) { - bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]); - } + if (pos != -1 && is_a_number_list(ua->argv[pos])) { + db_list_ctx jobids; + pm_strcpy(jobids.list, ua->argv[pos]); + bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, &jobids); } else { /* update cache for all jobids */ bvfs_update_cache(ua->jcr, ua->db); diff --git a/bacula/src/dird/ua_output.c b/bacula/src/dird/ua_output.c index c93ad9d2d4..55c308966a 100644 --- a/bacula/src/dird/ua_output.c +++ b/bacula/src/dird/ua_output.c @@ -483,7 +483,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) } list_nextvol(ua, n); } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) { - char *jobids=NULL; + char *jobids = NULL; uint32_t limit=0; for (j=i+1; jargc; j++) { if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) { diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index 5eae29b5c0..e9e6a31e23 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2002-2008 Free Software Foundation Europe e.V. + Copyright (C) 2002-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -556,6 +556,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) char *fname; int len; bool gui_save; + db_list_ctx jobids; start_prompt(ua, _("To select the JobIds, you have the following choices:\n")); for (int i=0; list[i]; i++) { @@ -752,9 +753,10 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) return 0; } jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ - if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, rx->JobIds)) { + if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) { return 0; } + pm_strcpy(rx->JobIds, jobids.list); Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds); break; case 12: /* Cancel or quit */ diff --git a/bacula/src/dird/vbackup.c b/bacula/src/dird/vbackup.c index 45a1f7e065..e75d08d06c 100644 --- a/bacula/src/dird/vbackup.c +++ b/bacula/src/dird/vbackup.c @@ -50,7 +50,7 @@ static const int dbglevel = 10; -static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids); +static bool create_bootstrap_file(JCR *jcr, char *jobids); void vbackup_cleanup(JCR *jcr, int TermCode); /* @@ -135,6 +135,7 @@ bool do_vbackup(JCR *jcr) char ed1[100]; BSOCK *sd; char *p; + db_list_ctx jobids; Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->rstorage, jcr->wstorage); Dmsg2(100, "Read store=%s, write store=%s\n", @@ -157,28 +158,27 @@ bool do_vbackup(JCR *jcr) _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n")); } - POOLMEM *jobids = get_pool_memory(PM_FNAME); jcr->jr.JobLevel = L_VIRTUAL_FULL; - db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids); - jcr->jr.JobLevel = L_FULL; - Dmsg1(10, "Accurate jobids=%s\n", jobids); - if (*jobids == 0) { - free_pool_memory(jobids); + db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids); + Dmsg1(10, "Accurate jobids=%s\n", jobids.list); + if (jobids.count == 0) { Jmsg(jcr, M_FATAL, 0, _("No previous Jobs found.\n")); return false; } + jcr->jr.JobLevel = L_FULL; + /* * Now we find the last job that ran and store it's info in * the previous_jr record. We will set our times to the * values from that job so that anything changed after that * time will be picked up on the next backup. */ - p = strrchr(jobids, ','); /* find last jobid */ + p = strrchr(jobids.list, ','); /* find last jobid */ if (p != NULL) { p++; } else { - p = jobids; + p = jobids.list; } memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr)); jcr->previous_jr.JobId = str_to_int64(p); @@ -189,12 +189,10 @@ _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n") return false; } - if (!create_bootstrap_file(jcr, jobids)) { + if (!create_bootstrap_file(jcr, jobids.list)) { Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n")); - free_pool_memory(jobids); return false; } - free_pool_memory(jobids); /* * Open a message channel connection with the Storage @@ -476,7 +474,7 @@ int insert_bootstrap_handler(void *ctx, int num_fields, char **row) } -static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids) +static bool create_bootstrap_file(JCR *jcr, char *jobids) { RESTORE_CTX rx; UAContext *ua; diff --git a/bacula/src/lib/edit.c b/bacula/src/lib/edit.c index 8b13435f5f..a26ca61aba 100644 --- a/bacula/src/lib/edit.c +++ b/bacula/src/lib/edit.c @@ -1,14 +1,7 @@ -/* - * edit.c edit string to ascii, and ascii to internal - * - * Kern Sibbald, December MMII - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution - Copyright (C) 2002-2006 Free Software Foundation Europe e.V. + Copyright (C) 2002-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -32,6 +25,13 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * edit.c edit string to ascii, and ascii to internal + * + * Kern Sibbald, December MMII + * + * Version $Id$ + */ #include "bacula.h" #include @@ -407,7 +407,7 @@ bool is_a_number(const char *n) } /* - * Check if specified string is a list of number or not + * Check if specified string is a list of numbers or not */ bool is_a_number_list(const char *n) { diff --git a/bacula/technotes b/bacula/technotes index 9738213471..1d0512d983 100644 --- a/bacula/technotes +++ b/bacula/technotes @@ -2,6 +2,8 @@ General: +30Aug09 +kes Fix bug #1355 Director crashes with double free in Accurate SQL query 28Aug09 kes Fix bug #1357 Verify jobs fail when job has zero files 26Aug09 -- 2.39.5