ScriptObject* TypeDescriber::describeMetadataInfo(PoolObject* pool, uint32_t metadata_index) { AvmCore* core = m_toplevel->core(); const uint8_t* metadata_pos = pool->metadata_infos[metadata_index]; const uint32_t name_index = (metadata_pos) ? AvmCore::readU30(metadata_pos) : 0; // A bit of a hack: if the pool is builtin, always omit metadata chunks with names of "Version" // or "native", since these are used for reserved purposes internally. Stringp name = poolstr(pool, name_index); AvmAssert(name->isInterned() && core->kVersion->isInterned() && str(kstrid_native)->isInterned()); if (pool->isBuiltin && (name == core->kVersion || name == str(kstrid_native))) return NULL; const uint32_t val_count = (metadata_pos) ? AvmCore::readU30(metadata_pos) : 0; ScriptObject* o = new_object(); ArrayObject* a = new_array(); if (val_count > 0) { GC* gc = core->GetGC(); List<uint32_t> key_indexes(gc); List<uint32_t> val_indexes(gc); read_u30_list(key_indexes, val_count, metadata_pos); read_u30_list(val_indexes, val_count, metadata_pos); for (uint32_t i = 0; i < val_count; ++i) { ScriptObject* v = new_object(); const KVPair props[] = { { kstrid_key, strAtom(poolstr(pool, key_indexes.get(i))) }, { kstrid_value, strAtom(poolstr(pool, val_indexes.get(i))) }, }; setpropmulti(v, props, elem_count(props)); pushobj(a, v); } } const KVPair props[] = { { kstrid_name, strAtom(name) }, { kstrid_value, objAtom(a) }, }; setpropmulti(o, props, elem_count(props)); return o; }
ArrayObject* TypeDescriber::describeParams(const AbstractFunction* af) { ArrayObject* a = new_array(); const int requiredParamCount = af->requiredParamCount(); for (int i = 1; i <= af->param_count; ++i) { ScriptObject* v = new_object(); const KVPair props[] = { { kstrid_type, strAtom(describeClassName(af->paramTraits(i))) }, { kstrid_optional, boolAtom(i > requiredParamCount) }, }; setpropmulti(v, props, elem_count(props)); pushobj(a, v); } return a; }
static int prepare_hashbang( char **mapped_file, /* In: script, out: mapped script interpreter */ char *orig_file, char ***argvp, char ***envpp, const char *exec_policy_name) { int argc, fd, c, i, j, n; char ch; char *mapped_interpreter = NULL; char **new_argv = NULL; char hashbang[SBOX_MAXPATH]; /* only 60 needed on linux, just be safe */ char interpreter[SBOX_MAXPATH]; char *interp_arg = NULL; char *tmp = NULL, *mapped_binaryname = NULL; int result = 0; if ((fd = open_nomap(*mapped_file, O_RDONLY)) < 0) { /* unexpected error, just run it */ return 0; } if ((c = read(fd, &hashbang[0], SBOX_MAXPATH - 1)) < 2) { /* again unexpected error, close fd and run it */ close_nomap_nolog(fd); return 0; } argc = elem_count(*argvp); /* extra element for hashbang argument */ new_argv = calloc(argc + 3, sizeof(char *)); /* skip any initial whitespace following "#!" */ for (i = 2; (hashbang[i] == ' ' || hashbang[i] == '\t') && i < c; i++) ; for (n = 0, j = i; i < c; i++) { ch = hashbang[i]; if (hashbang[i] == 0 || hashbang[i] == ' ' || hashbang[i] == '\t' || hashbang[i] == '\n') { hashbang[i] = 0; if (i > j) { if (n == 0) { char *ptr = &hashbang[j]; strcpy(interpreter, ptr); new_argv[n++] = strdup(interpreter); } else { /* this was the one and only * allowed argument for the * interpreter */ interp_arg = strdup(&hashbang[j]); new_argv[n++] = interp_arg; break; } } j = i + 1; } if (ch == '\n' || ch == 0) break; } new_argv[n++] = strdup(orig_file); /* the unmapped script path */ for (i = 1; (*argvp)[i] != NULL && i < argc; ) { new_argv[n++] = (*argvp)[i++]; } new_argv[n] = NULL; /* Now we need to update __SB2_ORIG_BINARYNAME to point to * the unmapped script interpreter (exec_map_script_interpreter * may change it again (not currently, but in the future) */ change_environment_variable( *envpp, "__SB2_ORIG_BINARYNAME=", interpreter); /* script interpreter mapping in C */ exec_policy_handle_t eph = find_exec_policy_handle(exec_policy_name); int c_mapping_result_code; const char *c_new_exec_policy_name = NULL; c_mapping_result_code = exec_map_script_interpreter(eph, exec_policy_name, interpreter, interp_arg, *mapped_file, orig_file, new_argv, &c_new_exec_policy_name, &mapped_interpreter); SB_LOG(SB_LOGLEVEL_DEBUG, "back from exec_map_script_interpreter => %d (%s)", c_mapping_result_code, (mapped_interpreter ? mapped_interpreter : "<NULL>")); switch (c_mapping_result_code) { case 0: /* exec arguments were modified, argv has been modified */ SB_LOG(SB_LOGLEVEL_DEBUG, "%s: <0> argv has been updated", __func__); break; case 1: SB_LOG(SB_LOGLEVEL_DEBUG, "%s: <1> argv was not modified", __func__); break; case 2: SB_LOG(SB_LOGLEVEL_DEBUG, "%s: <2> Use ordinary path mapping", __func__); if (mapped_interpreter) free(mapped_interpreter); mapped_interpreter = NULL; { mapping_results_t mapping_result; clear_mapping_results_struct(&mapping_result); sbox_map_path_for_exec("script_interp", interpreter, &mapping_result); if (mapping_result.mres_result_buf) { mapped_interpreter = strdup(mapping_result.mres_result_buf); } if (mapping_result.mres_exec_policy_name) c_new_exec_policy_name = strdup(mapping_result.mres_exec_policy_name); else c_new_exec_policy_name = NULL; free_mapping_results(&mapping_result); } SB_LOG(SB_LOGLEVEL_DEBUG, "%s: " "interpreter=%s mapped_interpreter=%s policy=%s", __func__, interpreter, mapped_interpreter, c_new_exec_policy_name ? c_new_exec_policy_name : "NULL"); break; case -1: SB_LOG(SB_LOGLEVEL_DEBUG, "%s: <-1> exec denied", __func__); if (mapped_interpreter) free(mapped_interpreter); mapped_interpreter = NULL; return(-1); default: SB_LOG(SB_LOGLEVEL_ERROR, "%s: Unsupported result %d", __func__, c_mapping_result_code); return(-1); } exec_policy_name = c_new_exec_policy_name; if (!mapped_interpreter) { SB_LOG(SB_LOGLEVEL_ERROR, "failed to map script interpreter=%s", interpreter); return(-1); } /* * Binaryname (the one expected by the rules) comes still from * the interpreter name so we set it here. Note that it is now * basename of the mapped interpreter (not the original one)! */ tmp = strdup(mapped_interpreter); mapped_binaryname = strdup(basename(tmp)); change_environment_variable(*envpp, "__SB2_BINARYNAME=", mapped_binaryname); free(mapped_binaryname); free(tmp); SB_LOG(SB_LOGLEVEL_DEBUG, "prepare_hashbang(): interpreter=%s," "mapped_interpreter=%s", interpreter, mapped_interpreter); /* feed this through prepare_exec to let it deal with * cpu transparency etc. */ result = prepare_exec("run_hashbang", exec_policy_name, mapped_interpreter, 1/*file_has_been_mapped, and rue&policy exist*/, new_argv, *envpp, (enum binary_type*)NULL, mapped_file, argvp, envpp); SB_LOG(SB_LOGLEVEL_DEBUG, "prepare_hashbang done: mapped_file='%s'", *mapped_file); return(result); }
ScriptObject* TypeDescriber::describeTraits(Traitsp traits, uint32_t flags) { if (!(flags & INCLUDE_TRAITS)) return NULL; AvmCore* core = m_toplevel->core(); GC* gc = core->GetGC(); TraitsBindingsp tb = traits->getTraitsBindings(); TraitsMetadatap tm = traits->getTraitsMetadata(); ScriptObject* o = new_object(); ArrayObject* bases = NULL; ArrayObject* metadata = NULL; ArrayObject* interfaces = NULL; ArrayObject* methods = NULL; ArrayObject* accessors = NULL; ArrayObject* variables = NULL; ScriptObject* constructor = NULL; if (flags & INCLUDE_BASES) { metadata = new_array(); PoolObject* class_mdpool; const uint8_t* class_md = tm->getMetadataPos(class_mdpool); if (class_md) addDescribeMetadata(metadata, class_mdpool, class_md); } if (flags & INCLUDE_BASES) { bases = new_array(); for (Traitsp b = traits->base; b; b = b->base) pushstr(bases, describeClassName(b)); } if (flags & INCLUDE_INTERFACES) { interfaces = new_array(); // our TraitsBindings only includes our own interfaces, not any we might have inherited, // so walk the tree. there might be redundant interfaces listed in the inheritance, // so use a list to remove dupes List<Traitsp> unique(gc); for (Traitsp b = traits; b; b = b->base) { TraitsBindingsp tbi = b->getTraitsBindings(); for (uint32_t i = 0; i < tbi->interfaceCapacity; ++i) { Traitsp ti = tbi->getInterface(i); if (ti && ti->isInterface && unique.indexOf(ti) < 0) { unique.add(ti); pushstr(interfaces, describeClassName(ti)); } } } } // constructor if (flags & INCLUDE_CONSTRUCTOR) { AbstractFunction* initMethod = traits->init; if (initMethod && initMethod->param_count) { constructor = describeParams(initMethod); } } if (flags & (INCLUDE_ACCESSORS | INCLUDE_METHODS | INCLUDE_VARIABLES)) { // recover slot/method metadata and method-declarer information. // make a flattened set of bindings so we don't have to check for overrides as we go. // This is not terribly efficient, but doesn't need to be. MultinameHashtable* mybind = new (gc) MultinameHashtable(); addBindings(mybind, tb, flags); // Don't want interface methods, so post-process and wipe out any // bindings that were added for (uint32_t i = 0; i < tb->interfaceCapacity; ++i) { Traitsp ti = tb->getInterface(i); if (ti && ti->isInterface) { TraitsBindingsp tbi = ti->getTraitsBindings(); for (int32_t index = 0; (index = tbi->next(index)) != 0; ) { Stringp name = tbi->keyAt(index); Namespacep ns = tbi->nsAt(index); mybind->add(name, ns, BIND_NONE); } } } // yuck, replicate buggy behavior in FP9/10 List<Namespacep> nsremoval(gc); if (flags & HIDE_NSURI_METHODS) { for (uint32_t i = 0; i < tb->interfaceCapacity; ++i) { Traitsp ti = tb->getInterface(i); // already did interfaces, don't need to do them again if (ti && !ti->isInterface) { TraitsBindingsp tbi = ti->getTraitsBindings(); for (int32_t index = 0; (index = tbi->next(index)) != 0; ) { Namespacep ns = tbi->nsAt(index); if (ns->getURI()->length() > 0 && nsremoval.indexOf(ns) < 0) nsremoval.add(ns); } } } } for (int32_t index = 0; (index = mybind->next(index)) != 0; ) { Binding binding = mybind->valueAt(index); Stringp name = mybind->keyAt(index); Namespacep ns = mybind->nsAt(index); Stringp nsuri = ns->getURI(); TraitsMetadata::MetadataPtr md1 = NULL; TraitsMetadata::MetadataPtr md2 = NULL; PoolObject* md1pool = NULL; PoolObject* md2pool = NULL; // We only display public members -- exposing private namespaces could compromise security. if (ns->getType() != Namespace::NS_Public) continue; if ((flags & HIDE_NSURI_METHODS) && nsremoval.indexOf(ns) >= 0) continue; ScriptObject* v = new_object(); const BindingKind bk = AvmCore::bindingKind(binding); switch (bk) { case BKIND_CONST: case BKIND_VAR: { if (!(flags & INCLUDE_VARIABLES)) continue; const uint32_t slotID = AvmCore::bindingToSlotId(binding); const KVPair props[] = { { kstrid_access, strAtom(str(bk == BKIND_CONST ? kstrid_readonly : kstrid_readwrite)) }, { kstrid_type, strAtom(describeClassName(tb->getSlotTraits(slotID))) }, }; setpropmulti(v, props, elem_count(props)); if (!variables) variables = new_array(); pushobj(variables, v); md1 = tm->getSlotMetadataPos(slotID, md1pool); break; } case BKIND_METHOD: { if (!(flags & INCLUDE_METHODS)) continue; const uint32_t methodID = AvmCore::bindingToMethodId(binding); const AbstractFunction* af = tb->getMethod(methodID); Traitsp declaringTraits = af->declaringTraits; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_returnType, strAtom(describeClassName(af->returnTraits())) }, { kstrid_parameters, objAtom(describeParams(af)) }, }; setpropmulti(v, props, elem_count(props)); if (!methods) methods = new_array(); pushobj(methods, v); md1 = tm->getMethodMetadataPos(methodID, md1pool); break; } case BKIND_GET: case BKIND_SET: case BKIND_GETSET: { if (!(flags & INCLUDE_ACCESSORS)) continue; const uint32_t methodID = AvmCore::hasGetterBinding(binding) ? AvmCore::bindingToGetterId(binding) : AvmCore::bindingToSetterId(binding); const AbstractFunction* af = tb->getMethod(methodID); Traitsp declaringTraits = af->declaringTraits; Traitsp accessorType = AvmCore::hasGetterBinding(binding) ? af->returnTraits() : af->paramTraits(1); static const uint8_t bk2str[8] = { uint8_t(kstrid_emptyString), // BKIND_NONE uint8_t(kstrid_emptyString), // BKIND_METHOD uint8_t(kstrid_emptyString), // BKIND_VAR uint8_t(kstrid_emptyString), // BKIND_CONST uint8_t(kstrid_emptyString), // BKIND_ITRAMP uint8_t(kstrid_readonly), // BKIND_GET uint8_t(kstrid_writeonly), // BKIND_SET uint8_t(kstrid_readwrite) // BKIND_GETSET }; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_access, strAtom(str(StringId(bk2str[bk]))) }, { kstrid_type, strAtom(describeClassName(accessorType)) }, }; setpropmulti(v, props, elem_count(props)); if (AvmCore::hasGetterBinding(binding)) md1 = tm->getMethodMetadataPos(AvmCore::bindingToGetterId(binding), md1pool); if (AvmCore::hasSetterBinding(binding)) md2 = tm->getMethodMetadataPos(AvmCore::bindingToSetterId(binding), md2pool); if (!accessors) accessors = new_array(); pushobj(accessors, v); break; } case BKIND_NONE: break; } ArrayObject* vm = NULL; if ((flags & INCLUDE_METADATA) && (md1 || md2)) { vm = new_array(); addDescribeMetadata(vm, md1pool, md1); addDescribeMetadata(vm, md2pool, md2); } const KVPair props[] = { { kstrid_name, strAtom(name) }, { kstrid_uri, strAtom(nsuri->length() == 0 ? NULL : nsuri) }, { kstrid_metadata, objAtom(vm) }, }; setpropmulti(v, props, elem_count(props)); } } const KVPair props[] = { { kstrid_bases, objAtom(bases) }, { kstrid_interfaces, objAtom(interfaces) }, { kstrid_metadata, objAtom(metadata) }, { kstrid_accessors, objAtom(accessors) }, { kstrid_methods, objAtom(methods) }, { kstrid_variables, objAtom(variables) }, { kstrid_constructor, objAtom(constructor) }, }; setpropmulti(o, props, elem_count(props)); return o; }