xpcom/components/ManifestParser.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/ArrayUtils.h"
     8 #include "ManifestParser.h"
    10 #include <string.h>
    12 #include "prio.h"
    13 #include "prprf.h"
    14 #if defined(XP_WIN)
    15 #include <windows.h>
    16 #elif defined(MOZ_WIDGET_COCOA)
    17 #include <CoreServices/CoreServices.h>
    18 #include "nsCocoaFeatures.h"
    19 #elif defined(MOZ_WIDGET_GTK)
    20 #include <gtk/gtk.h>
    21 #endif
    23 #ifdef MOZ_WIDGET_ANDROID
    24 #include "AndroidBridge.h"
    25 #endif
    27 #include "mozilla/Services.h"
    29 #include "nsCRT.h"
    30 #include "nsConsoleMessage.h"
    31 #include "nsTextFormatter.h"
    32 #include "nsVersionComparator.h"
    33 #include "nsXPCOMCIDInternal.h"
    35 #include "nsIConsoleService.h"
    36 #include "nsIScriptError.h"
    37 #include "nsIXULAppInfo.h"
    38 #include "nsIXULRuntime.h"
    40 using namespace mozilla;
    42 struct ManifestDirective
    43 {
    44   const char* directive;
    45   int argc;
    47   // Some directives should only be delivered for NS_COMPONENT_LOCATION
    48   // manifests.
    49   bool componentonly;
    51   bool ischrome;
    53   bool allowbootstrap;
    55   // The platform/contentaccessible flags only apply to content directives.
    56   bool contentflags;
    58   // Function to handle this directive. This isn't a union because C++ still
    59   // hasn't learned how to initialize unions in a sane way.
    60   void (nsComponentManagerImpl::*mgrfunc)
    61     (nsComponentManagerImpl::ManifestProcessingContext& cx,
    62      int lineno, char *const * argv);
    63   void (nsChromeRegistry::*regfunc)
    64     (nsChromeRegistry::ManifestProcessingContext& cx,
    65      int lineno, char *const *argv,
    66      bool platform, bool contentaccessible);
    68   bool isContract;
    69 };
    70 static const ManifestDirective kParsingTable[] = {
    71   { "manifest",         1, false, true, true, false,
    72     &nsComponentManagerImpl::ManifestManifest, nullptr },
    73   { "binary-component", 1, true, false, false, false,
    74     &nsComponentManagerImpl::ManifestBinaryComponent, nullptr },
    75   { "interfaces",       1, true, false, false, false,
    76     &nsComponentManagerImpl::ManifestXPT, nullptr },
    77   { "component",        2, true, false, false, false,
    78     &nsComponentManagerImpl::ManifestComponent, nullptr },
    79   { "contract",         2, true, false, false, false,
    80     &nsComponentManagerImpl::ManifestContract, nullptr, true},
    81   { "category",         3, true, false, false, false,
    82     &nsComponentManagerImpl::ManifestCategory, nullptr },
    83   { "content",          2, true, true, true,  true,
    84     nullptr, &nsChromeRegistry::ManifestContent },
    85   { "locale",           3, true, true, true,  false,
    86     nullptr, &nsChromeRegistry::ManifestLocale },
    87   { "skin",             3, false, true, true,  false,
    88     nullptr, &nsChromeRegistry::ManifestSkin },
    89   { "overlay",          2, true, true, false,  false,
    90     nullptr, &nsChromeRegistry::ManifestOverlay },
    91   { "style",            2, false, true, false,  false,
    92     nullptr, &nsChromeRegistry::ManifestStyle },
    93   { "override",         2, true, true, true,  false,
    94     nullptr, &nsChromeRegistry::ManifestOverride },
    95   { "resource",         2, true, true, false,  false,
    96     nullptr, &nsChromeRegistry::ManifestResource }
    97 };
    99 static const char kWhitespace[] = "\t ";
   101 static bool IsNewline(char c)
   102 {
   103   return c == '\n' || c == '\r';
   104 }
   106 namespace {
   107 struct AutoPR_smprintf_free
   108 {
   109   AutoPR_smprintf_free(char* buf)
   110     : mBuf(buf)
   111   {
   112   }
   114   ~AutoPR_smprintf_free()
   115   {
   116     if (mBuf)
   117       PR_smprintf_free(mBuf);
   118   }
   120   operator char*() const {
   121     return mBuf;
   122   }
   124   char* mBuf;
   125 };
   127 } // anonymous namespace
   129 void LogMessage(const char* aMsg, ...)
   130 {
   131   nsCOMPtr<nsIConsoleService> console =
   132     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   133   if (!console)
   134     return;
   136   va_list args;
   137   va_start(args, aMsg);
   138   AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args));
   139   va_end(args);
   141   nsCOMPtr<nsIConsoleMessage> error =
   142     new nsConsoleMessage(NS_ConvertUTF8toUTF16(formatted).get());
   143   console->LogMessage(error);
   144 }
   146 void LogMessageWithContext(FileLocation &aFile,
   147                            uint32_t aLineNumber, const char* aMsg, ...)
   148 {
   149   va_list args;
   150   va_start(args, aMsg);
   151   AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args));
   152   va_end(args);
   153   if (!formatted)
   154     return;
   156   nsCString file;
   157   aFile.GetURIString(file);
   159   nsCOMPtr<nsIScriptError> error =
   160     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
   161   if (!error) {
   162     // This can happen early in component registration. Fall back to a
   163     // generic console message.
   164     LogMessage("Warning: in '%s', line %i: %s", file.get(),
   165                aLineNumber, (char*) formatted);
   166     return;
   167   }
   169   nsCOMPtr<nsIConsoleService> console =
   170     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   171   if (!console)
   172     return;
   174   nsresult rv = error->Init(NS_ConvertUTF8toUTF16(formatted),
   175 			    NS_ConvertUTF8toUTF16(file), EmptyString(),
   176 			    aLineNumber, 0, nsIScriptError::warningFlag,
   177 			    "chrome registration");
   178   if (NS_FAILED(rv))
   179     return;
   181   console->LogMessage(error);
   182 }
   184 /**
   185  * Check for a modifier flag of the following forms:
   186  *   "flag"   (same as "true")
   187  *   "flag=yes|true|1"
   188  *   "flag="no|false|0"
   189  * @param aFlag The flag to compare.
   190  * @param aData The tokenized data to check; this is lowercased
   191  *              before being passed in.
   192  * @param aResult If the flag is found, the value is assigned here.
   193  * @return Whether the flag was handled.
   194  */
   195 static bool
   196 CheckFlag(const nsSubstring& aFlag, const nsSubstring& aData, bool& aResult)
   197 {
   198   if (!StringBeginsWith(aData, aFlag))
   199     return false;
   201   if (aFlag.Length() == aData.Length()) {
   202     // the data is simply "flag", which is the same as "flag=yes"
   203     aResult = true;
   204     return true;
   205   }
   207   if (aData.CharAt(aFlag.Length()) != '=') {
   208     // the data is "flag2=", which is not anything we care about
   209     return false;
   210   }
   212   if (aData.Length() == aFlag.Length() + 1) {
   213     aResult = false;
   214     return true;
   215   }
   217   switch (aData.CharAt(aFlag.Length() + 1)) {
   218   case '1':
   219   case 't': //true
   220   case 'y': //yes
   221     aResult = true;
   222     return true;
   224   case '0':
   225   case 'f': //false
   226   case 'n': //no
   227     aResult = false;
   228     return true;
   229   }
   231   return false;
   232 }
   234 enum TriState {
   235   eUnspecified,
   236   eBad,
   237   eOK
   238 };
   240 /**
   241  * Check for a modifier flag of the following form:
   242  *   "flag=string"
   243  *   "flag!=string"
   244  * @param aFlag The flag to compare.
   245  * @param aData The tokenized data to check; this is lowercased
   246  *              before being passed in.
   247  * @param aValue The value that is expected.
   248  * @param aResult If this is "ok" when passed in, this is left alone.
   249  *                Otherwise if the flag is found it is set to eBad or eOK.
   250  * @return Whether the flag was handled.
   251  */
   252 static bool
   253 CheckStringFlag(const nsSubstring& aFlag, const nsSubstring& aData,
   254                 const nsSubstring& aValue, TriState& aResult)
   255 {
   256   if (aData.Length() < aFlag.Length() + 1)
   257     return false;
   259   if (!StringBeginsWith(aData, aFlag))
   260     return false;
   262   bool comparison = true;
   263   if (aData[aFlag.Length()] != '=') {
   264     if (aData[aFlag.Length()] == '!' &&
   265         aData.Length() >= aFlag.Length() + 2 &&
   266         aData[aFlag.Length() + 1] == '=')
   267       comparison = false;
   268     else
   269       return false;
   270   }
   272   if (aResult != eOK) {
   273     nsDependentSubstring testdata = Substring(aData, aFlag.Length() + (comparison ? 1 : 2));
   274     if (testdata.Equals(aValue))
   275       aResult = comparison ? eOK : eBad;
   276     else
   277       aResult = comparison ? eBad : eOK;
   278   }
   280   return true;
   281 }
   283 /**
   284  * Check for a modifier flag of the following form:
   285  *   "flag=version"
   286  *   "flag<=version"
   287  *   "flag<version"
   288  *   "flag>=version"
   289  *   "flag>version"
   290  * @param aFlag The flag to compare.
   291  * @param aData The tokenized data to check; this is lowercased
   292  *              before being passed in.
   293  * @param aValue The value that is expected. If this is empty then no
   294  *               comparison will match.
   295  * @param aResult If this is eOK when passed in, this is left alone.
   296  *                Otherwise if the flag is found it is set to eBad or eOK.
   297  * @return Whether the flag was handled.
   298  */
   300 #define COMPARE_EQ    1 << 0
   301 #define COMPARE_LT    1 << 1
   302 #define COMPARE_GT    1 << 2
   304 static bool
   305 CheckVersionFlag(const nsString& aFlag, const nsString& aData,
   306                  const nsString& aValue, TriState& aResult)
   307 {
   308   if (aData.Length() < aFlag.Length() + 2)
   309     return false;
   311   if (!StringBeginsWith(aData, aFlag))
   312     return false;
   314   if (aValue.Length() == 0) {
   315     if (aResult != eOK)
   316       aResult = eBad;
   317     return true;
   318   }
   320   uint32_t comparison;
   321   nsAutoString testdata;
   323   switch (aData[aFlag.Length()]) {
   324   case '=':
   325     comparison = COMPARE_EQ;
   326     testdata = Substring(aData, aFlag.Length() + 1);
   327     break;
   329   case '<':
   330     if (aData[aFlag.Length() + 1] == '=') {
   331       comparison = COMPARE_EQ | COMPARE_LT;
   332       testdata = Substring(aData, aFlag.Length() + 2);
   333     }
   334     else {
   335       comparison = COMPARE_LT;
   336       testdata = Substring(aData, aFlag.Length() + 1);
   337     }
   338     break;
   340   case '>':
   341     if (aData[aFlag.Length() + 1] == '=') {
   342       comparison = COMPARE_EQ | COMPARE_GT;
   343       testdata = Substring(aData, aFlag.Length() + 2);
   344     }
   345     else {
   346       comparison = COMPARE_GT;
   347       testdata = Substring(aData, aFlag.Length() + 1);
   348     }
   349     break;
   351   default:
   352     return false;
   353   }
   355   if (testdata.Length() == 0)
   356     return false;
   358   if (aResult != eOK) {
   359     int32_t c = mozilla::CompareVersions(NS_ConvertUTF16toUTF8(aValue).get(),
   360                                          NS_ConvertUTF16toUTF8(testdata).get());
   361     if ((c == 0 && comparison & COMPARE_EQ) ||
   362 	(c < 0 && comparison & COMPARE_LT) ||
   363 	(c > 0 && comparison & COMPARE_GT))
   364       aResult = eOK;
   365     else
   366       aResult = eBad;
   367   }
   369   return true;
   370 }
   372 // In-place conversion of ascii characters to lower case
   373 static void
   374 ToLowerCase(char* token)
   375 {
   376   for (; *token; ++token)
   377     *token = NS_ToLower(*token);
   378 }
   380 namespace {
   382 struct CachedDirective
   383 {
   384   int lineno;
   385   char* argv[4];
   386 };
   388 } // anonymous namespace
   391 void
   392 ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOnly)
   393 {
   394   nsComponentManagerImpl::ManifestProcessingContext mgrcx(type, file, aChromeOnly);
   395   nsChromeRegistry::ManifestProcessingContext chromecx(type, file);
   396   nsresult rv;
   398   NS_NAMED_LITERAL_STRING(kPlatform, "platform");
   399   NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible");
   400   NS_NAMED_LITERAL_STRING(kApplication, "application");
   401   NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
   402   NS_NAMED_LITERAL_STRING(kGeckoVersion, "platformversion");
   403   NS_NAMED_LITERAL_STRING(kOs, "os");
   404   NS_NAMED_LITERAL_STRING(kOsVersion, "osversion");
   405   NS_NAMED_LITERAL_STRING(kABI, "abi");
   406 #if defined(MOZ_WIDGET_ANDROID)
   407   NS_NAMED_LITERAL_STRING(kTablet, "tablet");
   408 #endif
   410   // Obsolete
   411   NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers");
   413   nsAutoString appID;
   414   nsAutoString appVersion;
   415   nsAutoString geckoVersion;
   416   nsAutoString osTarget;
   417   nsAutoString abi;
   419   nsCOMPtr<nsIXULAppInfo> xapp (do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
   420   if (xapp) {
   421     nsAutoCString s;
   422     rv = xapp->GetID(s);
   423     if (NS_SUCCEEDED(rv))
   424       CopyUTF8toUTF16(s, appID);
   426     rv = xapp->GetVersion(s);
   427     if (NS_SUCCEEDED(rv))
   428       CopyUTF8toUTF16(s, appVersion);
   430     rv = xapp->GetPlatformVersion(s);
   431     if (NS_SUCCEEDED(rv))
   432       CopyUTF8toUTF16(s, geckoVersion);
   434     nsCOMPtr<nsIXULRuntime> xruntime (do_QueryInterface(xapp));
   435     if (xruntime) {
   436       rv = xruntime->GetOS(s);
   437       if (NS_SUCCEEDED(rv)) {
   438         ToLowerCase(s);
   439         CopyUTF8toUTF16(s, osTarget);
   440       }
   442       rv = xruntime->GetXPCOMABI(s);
   443       if (NS_SUCCEEDED(rv) && osTarget.Length()) {
   444         ToLowerCase(s);
   445         CopyUTF8toUTF16(s, abi);
   446         abi.Insert(char16_t('_'), 0);
   447         abi.Insert(osTarget, 0);
   448       }
   449     }
   450   }
   452   nsAutoString osVersion;
   453 #if defined(XP_WIN)
   454 #pragma warning(push)
   455 #pragma warning(disable:4996) // VC12+ deprecates GetVersionEx
   456   OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
   457   if (GetVersionEx(&info)) {
   458     nsTextFormatter::ssprintf(osVersion, MOZ_UTF16("%ld.%ld"),
   459                                          info.dwMajorVersion,
   460                                          info.dwMinorVersion);
   461   }
   462 #pragma warning(pop)
   463 #elif defined(MOZ_WIDGET_COCOA)
   464   SInt32 majorVersion = nsCocoaFeatures::OSXVersionMajor();
   465   SInt32 minorVersion = nsCocoaFeatures::OSXVersionMinor();
   466   nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(),
   467                                        majorVersion,
   468                                        minorVersion);
   469 #elif defined(MOZ_WIDGET_GTK)
   470   nsTextFormatter::ssprintf(osVersion, MOZ_UTF16("%ld.%ld"),
   471                                        gtk_major_version,
   472                                        gtk_minor_version);
   473 #elif defined(MOZ_WIDGET_ANDROID)
   474   bool isTablet = false;
   475   if (mozilla::AndroidBridge::Bridge()) {
   476     mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", "RELEASE", osVersion);
   477     isTablet = mozilla::widget::android::GeckoAppShell::IsTablet();
   478   }
   479 #endif
   481   // Because contracts must be registered after CIDs, we save and process them
   482   // at the end.
   483   nsTArray<CachedDirective> contracts;
   485   char *token;
   486   char *newline = buf;
   487   uint32_t line = 0;
   489   // outer loop tokenizes by newline
   490   while (*newline) {
   491     while (*newline && IsNewline(*newline)) {
   492       ++newline;
   493       ++line;
   494     }
   495     if (!*newline)
   496       break;
   498     token = newline;
   499     while (*newline && !IsNewline(*newline))
   500       ++newline;
   502     if (*newline) {
   503       *newline = '\0';
   504       ++newline;
   505     }
   506     ++line;
   508     if (*token == '#') // ignore lines that begin with # as comments
   509       continue;
   511     char *whitespace = token;
   512     token = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
   513     if (!token) continue;
   515     const ManifestDirective* directive = nullptr;
   516     for (const ManifestDirective* d = kParsingTable;
   517 	 d < ArrayEnd(kParsingTable);
   518 	 ++d) {
   519       if (!strcmp(d->directive, token)) {
   520 	directive = d;
   521 	break;
   522       }
   523     }
   525     if (!directive) {
   526       LogMessageWithContext(file, line,
   527                             "Ignoring unrecognized chrome manifest directive '%s'.",
   528                             token);
   529       continue;
   530     }
   532     if (!directive->allowbootstrap && NS_BOOTSTRAPPED_LOCATION == type) {
   533       LogMessageWithContext(file, line,
   534                             "Bootstrapped manifest not allowed to use '%s' directive.",
   535                             token);
   536       continue;
   537     }
   539     if (directive->componentonly && NS_SKIN_LOCATION == type) {
   540       LogMessageWithContext(file, line,
   541                             "Skin manifest not allowed to use '%s' directive.",
   542                             token);
   543       continue;
   544     }
   546     NS_ASSERTION(directive->argc < 4, "Need to reset argv array length");
   547     char* argv[4];
   548     for (int i = 0; i < directive->argc; ++i)
   549       argv[i] = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
   551     if (!argv[directive->argc - 1]) {
   552       LogMessageWithContext(file, line,
   553                             "Not enough arguments for chrome manifest directive '%s', expected %i.",
   554                             token, directive->argc);
   555       continue;
   556     }
   558     bool ok = true;
   559     TriState stAppVersion = eUnspecified;
   560     TriState stGeckoVersion = eUnspecified;
   561     TriState stApp = eUnspecified;
   562     TriState stOsVersion = eUnspecified;
   563     TriState stOs = eUnspecified;
   564     TriState stABI = eUnspecified;
   565 #if defined(MOZ_WIDGET_ANDROID)
   566     TriState stTablet = eUnspecified;
   567 #endif
   568     bool platform = false;
   569     bool contentAccessible = false;
   571     while (nullptr != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && ok) {
   572       ToLowerCase(token);
   573       NS_ConvertASCIItoUTF16 wtoken(token);
   575       if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
   576           CheckStringFlag(kOs, wtoken, osTarget, stOs) ||
   577           CheckStringFlag(kABI, wtoken, abi, stABI) ||
   578           CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
   579           CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
   580           CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion))
   581         continue;
   583 #if defined(MOZ_WIDGET_ANDROID)
   584       bool tablet = false;
   585       if (CheckFlag(kTablet, wtoken, tablet)) {
   586         stTablet = (tablet == isTablet) ? eOK : eBad;
   587         continue;
   588       }
   589 #endif
   591       if (directive->contentflags &&
   592           (CheckFlag(kPlatform, wtoken, platform) ||
   593            CheckFlag(kContentAccessible, wtoken, contentAccessible)))
   594         continue;
   596       bool xpcNativeWrappers = true; // Dummy for CheckFlag.
   597       if (CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers)) {
   598         LogMessageWithContext(file, line,
   599                               "Ignoring obsolete chrome registration modifier '%s'.",
   600                               token);
   601         continue;
   602       }
   604       LogMessageWithContext(file, line,
   605                             "Unrecognized chrome manifest modifier '%s'.",
   606                             token);
   607       ok = false;
   608     }
   610     if (!ok ||
   611         stApp == eBad ||
   612         stAppVersion == eBad ||
   613         stGeckoVersion == eBad ||
   614         stOs == eBad ||
   615         stOsVersion == eBad ||
   616 #ifdef MOZ_WIDGET_ANDROID
   617         stTablet == eBad ||
   618 #endif
   619         stABI == eBad)
   620       continue;
   622     if (directive->regfunc) {
   623       if (GeckoProcessType_Default != XRE_GetProcessType())
   624         continue;
   626       if (!nsChromeRegistry::gChromeRegistry) {
   627         nsCOMPtr<nsIChromeRegistry> cr =
   628           mozilla::services::GetChromeRegistryService();
   629         if (!nsChromeRegistry::gChromeRegistry) {
   630           LogMessageWithContext(file, line,
   631                                 "Chrome registry isn't available yet.");
   632           continue;
   633         }
   634       }
   636       (nsChromeRegistry::gChromeRegistry->*(directive->regfunc))
   637 	(chromecx, line, argv, platform, contentAccessible);
   638     }
   639     else if (directive->ischrome || !aChromeOnly) {
   640       if (directive->isContract) {
   641         CachedDirective* cd = contracts.AppendElement();
   642         cd->lineno = line;
   643         cd->argv[0] = argv[0];
   644         cd->argv[1] = argv[1];
   645       }
   646       else
   647         (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))
   648           (mgrcx, line, argv);
   649     }
   650   }
   652   for (uint32_t i = 0; i < contracts.Length(); ++i) {
   653     CachedDirective& d = contracts[i];
   654     nsComponentManagerImpl::gComponentManager->ManifestContract
   655       (mgrcx, d.lineno, d.argv);
   656   }
   657 }

mercurial