@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Enforce that global locks have keys shorter than 64 characters

Summary:
Fixes T7484. There's a bunch of spooky mystery here but the current behavior can probably cause problems in at least some situations.

Also moves a couple callsigns to monograms (see T4245).

Test Plan:
- Faked a short lock length to hit the exception.
- Updated normally.
- Grepped for other use sites, none seemed suspicious or likely to overflow the lock length.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7484

Differential Revision: https://secure.phabricator.com/D12263

+65 -6
+1
src/__phutil_library_map__.php
··· 5235 5235 'PhabricatorHandleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 5236 5236 'PhabricatorHarbormasterApplication' => 'PhabricatorApplication', 5237 5237 'PhabricatorHarbormasterConfigOptions' => 'PhabricatorApplicationConfigOptions', 5238 + 'PhabricatorHash' => 'Phobject', 5238 5239 'PhabricatorHashTestCase' => 'PhabricatorTestCase', 5239 5240 'PhabricatorHelpApplication' => 'PhabricatorApplication', 5240 5241 'PhabricatorHelpController' => 'PhabricatorController',
+2 -3
src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php
··· 51 51 } 52 52 53 53 $repository = head($repos); 54 - $callsign = $repository->getCallsign(); 55 54 56 55 try { 57 - $lock_name = get_class($this).':'.$callsign; 56 + $lock_name = 'repository.update:'.$repository->getID(); 58 57 $lock = PhabricatorGlobalLock::newLock($lock_name); 59 58 60 59 $lock->lock(); ··· 135 134 $proxy = new PhutilProxyException( 136 135 pht( 137 136 'Error while pushing "%s" repository to mirrors.', 138 - $repository->getCallsign()), 137 + $repository->getMonogram()), 139 138 $ex); 140 139 phlog($proxy); 141 140 }
+14 -1
src/infrastructure/util/PhabricatorGlobalLock.php
··· 39 39 40 40 public static function newLock($name) { 41 41 $namespace = PhabricatorLiskDAO::getStorageNamespace(); 42 - $full_name = 'global:'.$namespace.':'.$name; 42 + $namespace = PhabricatorHash::digestToLength($namespace, 20); 43 + 44 + $full_name = $namespace.'-g:'.$name; 45 + 46 + $length_limit = 64; 47 + if (strlen($full_name) > $length_limit) { 48 + throw new Exception( 49 + pht( 50 + 'Lock name "%s" is too long (full lock name is "%s"). The '. 51 + 'full lock name must not be longer than %s bytes.', 52 + $name, 53 + $full_name, 54 + new PhutilNumber($length_limit))); 55 + } 43 56 44 57 $lock = self::getLock($full_name); 45 58 if (!$lock) {
+48 -2
src/infrastructure/util/PhabricatorHash.php
··· 1 1 <?php 2 2 3 - final class PhabricatorHash { 3 + final class PhabricatorHash extends Phobject { 4 4 5 + const INDEX_DIGEST_LENGTH = 12; 5 6 6 7 /** 7 8 * Digest a string for general use, including use which relates to security. ··· 68 69 } 69 70 70 71 $result = ''; 71 - for ($ii = 0; $ii < 12; $ii++) { 72 + for ($ii = 0; $ii < self::INDEX_DIGEST_LENGTH; $ii++) { 72 73 $result .= $map[(ord($hash[$ii]) & 0x3F)]; 73 74 } 74 75 75 76 return $result; 76 77 } 78 + 79 + 80 + /** 81 + * Shorten a string to a maximum byte length in a collision-resistant way 82 + * while retaining some degree of human-readability. 83 + * 84 + * This function converts an input string into a prefix plus a hash. For 85 + * example, a very long string beginning with "crabapplepie..." might be 86 + * digested to something like "crabapp-N1wM1Nz3U84k". 87 + * 88 + * This allows the maximum length of identifiers to be fixed while 89 + * maintaining a high degree of collision resistance and a moderate degree 90 + * of human readability. 91 + * 92 + * @param string The string to shorten. 93 + * @param int Maximum length of the result. 94 + * @return string String shortened in a collision-resistant way. 95 + */ 96 + public static function digestToLength($string, $length) { 97 + // We need at least two more characters than the hash length to fit in a 98 + // a 1-character prefix and a separator. 99 + $min_length = self::INDEX_DIGEST_LENGTH + 2; 100 + if ($length < $min_length) { 101 + throw new Exception( 102 + pht( 103 + 'Length parameter in digestToLength() must be at least %s, '. 104 + 'but %s was provided.', 105 + new PhutilNumber($min_length), 106 + new PhutilNumber($length))); 107 + } 108 + 109 + // We could conceivably return the string unmodified if it's shorter than 110 + // the specified length. Instead, always hash it. This makes the output of 111 + // the method more recognizable and consistent (no surprising new behavior 112 + // once you hit a string longer than `$length`) and prevents an attacker 113 + // who can control the inputs from intentionally using the hashed form 114 + // of a string to cause a collision. 115 + 116 + $hash = PhabricatorHash::digestForIndex($string); 117 + 118 + $prefix = substr($string, 0, ($length - ($min_length - 1))); 119 + 120 + return $prefix.'-'.$hash; 121 + } 122 + 77 123 78 124 }