internal static void WipeActiveFlags(SVX_MSG msg) { if (msg != null) { msg.active = false; // FIXME: What if a field contains an object whose dynamic type // contains more nested messages than the static type of the // field? We should fix this for all FieldFinder callers at // once. ~ REDACTED 2016-08-16 foreach (var acc in FieldFinder <SVX_MSG> .FindFields(msg.GetType(), // We do our own recursion in matches since it's cleaner. false)) { WipeActiveFlags(acc.nullConditionalGetter(msg)); } } }
private static SymT GatherUsefulSymTs(SVX_MSG msg) { // Want to warn if the msg is inactive but has a symT set, at least // for the root message (possible developer mistake)? SymT rootSymT = msg.active ? (SymT)msg.SVX_symT : null; var nestedSymTs = ( // NOTE: This will traverse into PayloadSecrets that contain // messages, which is what we want. from acc in FieldFinder <SVX_MSG> .FindFields(msg.GetType(), // We do our own recursion in matches. false) let nestedMsg = acc.nullConditionalGetter(msg) where nestedMsg != null let nestedSymT = GatherUsefulSymTs(nestedMsg) where nestedSymT != null select new NestedSymTEntry { fieldPath = acc.path, symT = nestedSymT } ).ToArray(); // As a simplification, don't unnecessarily create composites // (though it shouldn't break anything). And if we have no // information, return null so an outer message doesn't // unnecessarily create a composite. if (nestedSymTs.Length == 0) { return(rootSymT); // may be null } if (rootSymT == null) { rootSymT = new SymTNondet { messageTypeFullName = msg.GetType().FullName } } ; return(new SymTComposite { RootSymTWithMessageId = rootSymT, nestedSymTs = nestedSymTs }); }
string EmitMessage(SymT symT, MessageReuseScope scope) { // First look in the scope. This mechanism currently handles cases // where a later argument to a method call is derived from an // earlier one; that's enough for the authorization code flow example. string reuseVarName; for (var checkScope = scope; checkScope != null; checkScope = checkScope.outer) { if (checkScope.availableMessages.TryGetValue(symT.messageId, out reuseVarName)) { return(reuseVarName); } } // Otherwise not found; proceed. // XXX: Currently we cannot deal with non-public participant classes // and methods. It's an open question if we should be able to and // how it should be implemented. I guess one way is for developers // to write [InternalsVisibleTo("VProgram")]. There shouldn't be // much risk of abuse because for the vProgram to access a program // element, someone authorized had to pass it to an SVX API. SymTNondet symTNondet; SymTMethod symTMethod; SymTComposite symTComposite; SymTTransfer symTTransfer; string outputVarName; // Consider using a visitor. It's not worth it yet. :/ if ((symTNondet = symT as SymTNondet) != null) { outputVarName = nextVar("msg"); // FIXME: Nondetting arbitrary data structures will cause // aliasing nightmares. We need to emit a specialized Nondet // for each message type. AppendFormattedLine("{0} {1} = SVX.VProgram_API.Nondet<{0}>();", FormatTypeFullName(symTNondet.messageTypeFullName), outputVarName); } else if ((symTMethod = symT as SymTMethod) != null) { var newScope = new MessageReuseScope { outer = scope }; var argVarNames = new List <string>(); for (int i = 0; i < symTMethod.methodArgTypeFullNames.Length; i++) { SymT inputSymT = symTMethod.inputSymTs[i]; string inputVarName; if (inputSymT != null) { inputVarName = EmitMessage(inputSymT, newScope); newScope.availableMessages[inputSymT.messageId] = inputVarName; } else { // Nondet non-message argument. One could argue for using a SymTNondet // even for non-message types. inputVarName = nextVar("arg"); AppendFormattedLine("{0} {1} = SVX.VProgram_API.Nondet<{0}>();", FormatTypeFullName(symTMethod.methodArgTypeFullNames[i]), inputVarName); } argVarNames.Add(inputVarName); } outputVarName = nextVar("msg"); AppendFormattedLine("{0} {1} = {2}.{3}({4});", FormatTypeFullName(symTMethod.methodReturnTypeFullName), outputVarName, EmitParticipant(symTMethod.participantId), symTMethod.methodName, string.Join(", ", argVarNames)); } else if ((symTComposite = symT as SymTComposite) != null) { SymTTransfer rootTransfer; if (symTComposite.rootSymT is SymTNondet) { // Nondet the root message and then overwrite the nested // messages that we have information about. outputVarName = EmitMessage(symTComposite.rootSymT, scope); foreach (var entry in symTComposite.nestedSymTs) { string nestedVarName = EmitMessage(entry.symT, scope); // Make sure we have a non-null parent to store the nested message in. var lastDot = entry.fieldPath.LastIndexOf('.'); if (lastDot != -1) { AppendFormattedLine("System.Diagnostics.Contracts.Contract.Assume({0} != null);", MakeFieldPathNullConditional(outputVarName, entry.fieldPath.Substring(0, lastDot), symTComposite.MessageTypeFullName)); } AppendFormattedLine("{0}.{1} = {2};", outputVarName, entry.fieldPath, nestedVarName); } } else if (symTComposite.rootSymT is SymTMethod) { // In this case, we assume the information in the rootSymT // subsumes the information in the nested SymTs and emit // code only for the rootSymT. If we had Equals for // messages, we could assume the nested messages equal, but // this is extra work which I don't believe is needed for // our examples so far. outputVarName = EmitMessage(symTComposite.rootSymT, scope); } else if ((rootTransfer = symTComposite.rootSymT as SymTTransfer) != null) { // The emitted code for the SymTTransfer will determine // whether the transfer is trusted. If so, just use the // transfer and ignore the nested messages, as in the // SymTMethod case. If not, it will use the fallback we // specify. outputVarName = EmitMessage(new SymTTransfer(rootTransfer) { fallback = new SymTComposite { RootSymTWithMessageId = new SymTNondet { messageTypeFullName = symTComposite.MessageTypeFullName }, nestedSymTs = symTComposite.nestedSymTs } }, scope); } else { // We should never have double composites. throw new NotImplementedException("Unhandled root SymT in composite"); } } else if ((symTTransfer = symT as SymTTransfer) != null) { string producerVarName = nextVar("producer"); AppendFormattedLine("SVX.Principal {0} = {1};", producerVarName, EmitPrincipalHandleOrNondet(symTTransfer.producer)); outputVarName = nextVar("msg"); AppendFormattedLine("{0} {1};", FormatTypeFullName(symTTransfer.MessageTypeFullName), outputVarName); AppendFormattedLine("if (SVX.VProgram_API.IsTrusted({0})) {{", producerVarName); { IncreaseIndent(); string inputVarName = EmitMessage(symTTransfer.originalSymT, scope); AppendFormattedLine("{0} = {1};", outputVarName, inputVarName); // To maintain soundness of the model, update the metadata // of nested messages the same way TransferNested would in // production, even though we don't use their SymTs. // XXX: symTTransfer.payloadSecretsVerifiedOnImport could // contain non-message payload secrets once we support them, // and we'd need to skip them here. foreach (var entry in symTTransfer.payloadSecretsVerifiedOnImport) { string paramsPath = entry.fieldPath + ".theParams"; AppendFormattedLine("System.Diagnostics.Contracts.Contract.Assume({0} != null);", MakeFieldPathNullConditional(outputVarName, paramsPath, symTTransfer.MessageTypeFullName)); AppendFormattedLine("SVX.SVX_Ops.TransferNested({0}.{1}, new {2}().Signer);", outputVarName, paramsPath, FormatTypeFullName(entry.secretGeneratorTypeFullName)); } DecreaseIndent(); } AppendFormattedLine("}} else {{"); { IncreaseIndent(); string inputVarName = EmitMessage(symTTransfer.fallback ?? new SymTNondet { messageTypeFullName = symTTransfer.MessageTypeFullName }, scope); AppendFormattedLine("{0} = {1};", outputVarName, inputVarName); DecreaseIndent(); } AppendFormattedLine("}}"); // Should we introduce a "transfer info" object that we can both // use as an arg to Transfer and store in the SymTTransfer, to // avoid shuttling individual arguments back and forth? The // following is not too bad. if (symTTransfer.hasSender) { // Note: Transfer does not use the realRequestProducer arg in the vProgram. AppendFormattedLine("SVX.SVX_Ops.Transfer({0}, {1}, {2}, null, {3});", outputVarName, producerVarName, EmitPrincipalHandleOrNondet(symTTransfer.sender), // http://stackoverflow.com/a/491367 :/ symTTransfer.browserOnly.ToString().ToLower()); } else { AppendFormattedLine("SVX.SVX_Ops.TransferNested({0}, {1});", outputVarName, producerVarName); } // If we verified secrets on import, they are valid regardless // of whether the transfer was trusted. foreach (var entry in symTTransfer.payloadSecretsVerifiedOnImport) { // I'm unsure of BCT handling of null dereferences in // general, but here we can definitely assume non-null. AppendFormattedLine("System.Diagnostics.Contracts.Contract.Assume({0} != null);", MakeFieldPathNullConditional(outputVarName, entry.fieldPath, symTTransfer.MessageTypeFullName)); // Follow the commented-out lines in // MessagePayloadSecretGenerator.VerifyAndExtract. I'd like // to define a helper method for this, but until I merge // SVX_Common and SVX_Ops, I'm between a rock and a hard // place because the method needs to be BCT translated but // needs to reference MessagePayloadSecretGenerator. AppendFormattedLine("System.Diagnostics.Contracts.Contract.Assume({0}.{1}.secretValue != null);", outputVarName, entry.fieldPath); AppendFormattedLine("System.Diagnostics.Contracts.Contract.Assume({0}.{1}.theParams != null);", outputVarName, entry.fieldPath); // XXX We're assuming the generator has a no-arg public // constructor and doesn't need any configuration parameters. AppendFormattedLine("SVX.VProgram_API.AssumeValidSecret({0}.{1}.secretValue, " + "{0}.{1}.theParams, new {2}().GetReaders({0}.{1}.theParams));", outputVarName, entry.fieldPath, FormatTypeFullName(entry.secretGeneratorTypeFullName)); } if (symTTransfer.hasSender) { Type messageType = GetTypeByFullName(symTTransfer.MessageTypeFullName); foreach (var acc in FieldFinder <Secret> .FindFields(messageType, true)) { AppendFormattedLine("SVX.VProgram_API.AssumeBorne({0}.SVX_producer, {1});", outputVarName, MakeFieldPathNullConditional(outputVarName, acc.path + ".secretValue", symTTransfer.MessageTypeFullName)); AppendFormattedLine("SVX.VProgram_API.AssumeBorne({0}.SVX_sender, {1});", outputVarName, MakeFieldPathNullConditional(outputVarName, acc.path + ".secretValue", symTTransfer.MessageTypeFullName)); } } } else { throw new NotImplementedException("Unhandled SymT"); } return(outputVarName); }