summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordgp <dgp@users.sourceforge.net>2017-12-05 15:52:55 (GMT)
committerdgp <dgp@users.sourceforge.net>2017-12-05 15:52:55 (GMT)
commit72f0f0b3468809e3a3a26e448b3bd3be8a8398a6 (patch)
tree8c8c8d840ac48b79e20f854642820a7c2cfdd268
parentf0d821a83cc98067d756402bebd4d941aa7e6cff (diff)
parentf3af86c67e61eaba34af3d25e883ae3e9fc83099 (diff)
downloadtcl-72f0f0b3468809e3a3a26e448b3bd3be8a8398a6.zip
tcl-72f0f0b3468809e3a3a26e448b3bd3be8a8398a6.tar.gz
tcl-72f0f0b3468809e3a3a26e448b3bd3be8a8398a6.tar.bz2
[4f6a1ebd64] Stop crash when same value passed to the -map and -subcommands options of ensemble configuration.
-rw-r--r--generic/tclEnsemble.c230
-rw-r--r--tests/namespace.test19
2 files changed, 133 insertions, 116 deletions
diff --git a/generic/tclEnsemble.c b/generic/tclEnsemble.c
index 972c33e..580ea5c 100644
--- a/generic/tclEnsemble.c
+++ b/generic/tclEnsemble.c
@@ -2432,13 +2432,31 @@ MakeCachedEnsembleCommand(
*/
static void
+ClearTable(
+ EnsembleConfig *ensemblePtr)
+{
+ Tcl_HashTable *hash = &ensemblePtr->subcommandTable;
+
+ if (hash->numEntries != 0) {
+ Tcl_HashSearch search;
+ Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(hash, &search);
+
+ while (hPtr != NULL) {
+ Tcl_Obj *prefixObj = Tcl_GetHashValue(hPtr);
+ Tcl_DecrRefCount(prefixObj);
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+ ckfree((char *) ensemblePtr->subcommandArrayPtr);
+ }
+ Tcl_DeleteHashTable(hash);
+}
+
+static void
DeleteEnsembleConfig(
ClientData clientData)
{
EnsembleConfig *ensemblePtr = clientData;
Namespace *nsPtr = ensemblePtr->nsPtr;
- Tcl_HashSearch search;
- Tcl_HashEntry *hEnt;
/*
* Unlink from the ensemble chain if it has not been marked as having been
@@ -2472,17 +2490,7 @@ DeleteEnsembleConfig(
* Kill the pointer-containing fields.
*/
- if (ensemblePtr->subcommandTable.numEntries != 0) {
- ckfree(ensemblePtr->subcommandArrayPtr);
- }
- hEnt = Tcl_FirstHashEntry(&ensemblePtr->subcommandTable, &search);
- while (hEnt != NULL) {
- Tcl_Obj *prefixObj = Tcl_GetHashValue(hEnt);
-
- Tcl_DecrRefCount(prefixObj);
- hEnt = Tcl_NextHashEntry(&search);
- }
- Tcl_DeleteHashTable(&ensemblePtr->subcommandTable);
+ ClearTable(ensemblePtr);
if (ensemblePtr->subcmdList != NULL) {
Tcl_DecrRefCount(ensemblePtr->subcmdList);
}
@@ -2538,107 +2546,101 @@ BuildEnsembleConfig(
int i, j, isNew;
Tcl_HashTable *hash = &ensemblePtr->subcommandTable;
Tcl_HashEntry *hPtr;
- Tcl_Obj *subcmdDictCopy = NULL ;
-
- if (hash->numEntries != 0) {
- /*
- * Remove pre-existing table.
- */
-
- ckfree(ensemblePtr->subcommandArrayPtr);
- hPtr = Tcl_FirstHashEntry(hash, &search);
- while (hPtr != NULL) {
- Tcl_Obj *prefixObj = Tcl_GetHashValue(hPtr);
-
- Tcl_DecrRefCount(prefixObj);
- hPtr = Tcl_NextHashEntry(&search);
- }
- Tcl_DeleteHashTable(hash);
- Tcl_InitHashTable(hash, TCL_STRING_KEYS);
- }
-
- /*
- * See if we've got an export list. If so, we will only export exactly
- * those commands, which may be either implemented by the prefix in the
- * subcommandDict or mapped directly onto the namespace's commands.
- */
-
- if (ensemblePtr->subcmdList != NULL) {
- Tcl_Obj **subcmdv, *target, *cmdObj, *cmdPrefixObj;
- int subcmdc;
-
- TclListObjGetElements(NULL, ensemblePtr->subcmdList, &subcmdc,
- &subcmdv);
- for (i=0 ; i<subcmdc ; i++) {
- const char *name = TclGetString(subcmdv[i]);
-
- hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
-
- /*
- * Skip non-unique cases.
- */
-
- if (!isNew) {
- continue;
- }
-
- /*
- * Look in our dictionary (if present) for the command.
- */
-
- if (ensemblePtr->subcommandDict != NULL) {
- if (subcmdDictCopy == NULL) {
- if (ensemblePtr->subcmdList == ensemblePtr->subcommandDict) {
- subcmdDictCopy = Tcl_DuplicateObj(ensemblePtr->subcommandDict);
- } else {
- subcmdDictCopy = ensemblePtr->subcommandDict;
- }
- Tcl_IncrRefCount(subcmdDictCopy);
- }
- Tcl_DictObjGet(NULL, subcmdDictCopy, subcmdv[i],
- &target);
- if (target != NULL) {
- Tcl_SetHashValue(hPtr, target);
- Tcl_IncrRefCount(target);
- continue;
- }
- }
-
- /*
- * Not there, so map onto the namespace. Note in this case that we
- * do not guarantee that the command is actually there; that is
- * the programmer's responsibility (or [::unknown] of course).
- */
-
- cmdObj = Tcl_NewStringObj(name, -1);
- cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
- Tcl_SetHashValue(hPtr, cmdPrefixObj);
- Tcl_IncrRefCount(cmdPrefixObj);
- }
- if (subcmdDictCopy != NULL) {
- Tcl_DecrRefCount(subcmdDictCopy);
- }
- } else if (ensemblePtr->subcommandDict != NULL) {
- /*
- * No subcmd list, but we do have a mapping dictionary so we should
- * use the keys of that. Convert the dictionary's contents into the
- * form required for the ensemble's internal hashtable.
- */
-
- Tcl_DictSearch dictSearch;
- Tcl_Obj *keyObj, *valueObj;
- int done;
-
- Tcl_DictObjFirst(NULL, ensemblePtr->subcommandDict, &dictSearch,
- &keyObj, &valueObj, &done);
- while (!done) {
- const char *name = TclGetString(keyObj);
-
- hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
- Tcl_SetHashValue(hPtr, valueObj);
- Tcl_IncrRefCount(valueObj);
- Tcl_DictObjNext(&dictSearch, &keyObj, &valueObj, &done);
- }
+ Tcl_Obj *mapDict = ensemblePtr->subcommandDict;
+ Tcl_Obj *subList = ensemblePtr->subcmdList;
+
+ ClearTable(ensemblePtr);
+ Tcl_InitHashTable(hash, TCL_STRING_KEYS);
+
+ if (subList) {
+ int subc;
+ Tcl_Obj **subv, *target, *cmdObj, *cmdPrefixObj;
+ char *name;
+
+ /*
+ * There is a list of exactly what subcommands go in the table.
+ * Must determine the target for each.
+ */
+
+ Tcl_ListObjGetElements(NULL, subList, &subc, &subv);
+ if (subList == mapDict) {
+ /*
+ * Strange case where explicit list of subcommands is same value
+ * as the dict mapping to targets.
+ */
+
+ for (i = 0; i < subc; i += 2) {
+ name = TclGetString(subv[i]);
+ hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
+ if (!isNew) {
+ cmdObj = (Tcl_Obj *)Tcl_GetHashValue(hPtr);
+ Tcl_DecrRefCount(cmdObj);
+ }
+ Tcl_SetHashValue(hPtr, subv[i+1]);
+ Tcl_IncrRefCount(subv[i+1]);
+
+ name = TclGetString(subv[i+1]);
+ hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
+ if (isNew) {
+ cmdObj = Tcl_NewStringObj(name, -1);
+ cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
+ Tcl_SetHashValue(hPtr, cmdPrefixObj);
+ Tcl_IncrRefCount(cmdPrefixObj);
+ }
+ }
+ } else {
+ /* Usual case where we can freely act on the list and dict. */
+
+ for (i = 0; i < subc; i++) {
+ name = TclGetString(subv[i]);
+ hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
+ if (!isNew) {
+ continue;
+ }
+
+ /* Lookup target in the dictionary */
+ if (mapDict) {
+ Tcl_DictObjGet(NULL, mapDict, subv[i], &target);
+ if (target) {
+ Tcl_SetHashValue(hPtr, target);
+ Tcl_IncrRefCount(target);
+ continue;
+ }
+ }
+
+ /*
+ * target was not in the dictionary so map onto the namespace.
+ * Note in this case that we do not guarantee that the
+ * command is actually there; that is the programmer's
+ * responsibility (or [::unknown] of course).
+ */
+ cmdObj = Tcl_NewStringObj(name, -1);
+ cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
+ Tcl_SetHashValue(hPtr, cmdPrefixObj);
+ Tcl_IncrRefCount(cmdPrefixObj);
+ }
+ }
+ } else if (mapDict) {
+ /*
+ * No subcmd list, but we do have a mapping dictionary so we should
+ * use the keys of that. Convert the dictionary's contents into the
+ * form required for the ensemble's internal hashtable.
+ */
+
+ Tcl_DictSearch dictSearch;
+ Tcl_Obj *keyObj, *valueObj;
+ int done;
+
+ Tcl_DictObjFirst(NULL, ensemblePtr->subcommandDict, &dictSearch,
+ &keyObj, &valueObj, &done);
+ while (!done) {
+ char *name = TclGetString(keyObj);
+
+ hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
+ Tcl_SetHashValue(hPtr, valueObj);
+ Tcl_IncrRefCount(valueObj);
+ Tcl_DictObjNext(&dictSearch, &keyObj, &valueObj, &done);
+ }
} else {
/*
* Discover what commands are actually exported by the namespace.
diff --git a/tests/namespace.test b/tests/namespace.test
index 5387ae8..9fa9331 100644
--- a/tests/namespace.test
+++ b/tests/namespace.test
@@ -1808,14 +1808,29 @@ test namespace-42.9 {
deallocated List struct.
} -setup {
namespace eval n {namespace ensemble create}
- dict set list one ::two
- namespace ensemble configure n -subcommands $list -map $list
+ set lst [dict create one ::two]
+ namespace ensemble configure n -subcommands $lst -map $lst
} -body {
n one
} -cleanup {
namespace delete n
+ unset -nocomplain lst
} -returnCodes error -match glob -result {invalid command name*}
+test namespace-42.10 {
+ ensembles: [Bug 4f6a1ebd64] segmentation fault due to pointer to a
+ deallocated List struct (this time with duplicate of one in "dict").
+} -setup {
+ namespace eval n {namespace ensemble create}
+ set lst [list one ::two one ::three]
+ namespace ensemble configure n -subcommands $lst -map $lst
+} -body {
+ n one
+} -cleanup {
+ namespace delete n
+ unset -nocomplain lst
+} -returnCodes error -match glob -result {invalid command name *three*}
+
test namespace-43.1 {ensembles: dict-driven} {
namespace eval ns {
namespace export x*