mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-14 02:41:24 +01:00
89d51d5f29
Because this exploit has been widely known for years and has not been fixed by Mojang, we decided that it was worth allowing people to toggle it on/off due to how easy it is to make it configurable. It should be noted that this decision does not promise all future exploits will be configurable.
126 lines
6.5 KiB
Diff
126 lines
6.5 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Sat, 11 Jul 2020 05:09:28 -0700
|
|
Subject: [PATCH] Fix GameProfileCache concurrency
|
|
|
|
Separate lookup and state access locks prevent lookups
|
|
from stalling simple state access/write calls
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
|
|
index 1ec0f3a7148c2f412421772f6e1dff0bb92a51bc..d45fe762941dd6da45c890706bc9e4c0438522c3 100644
|
|
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
|
|
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
|
|
@@ -60,6 +60,11 @@ public class GameProfileCache {
|
|
@Nullable
|
|
private Executor executor;
|
|
|
|
+ // Paper start - Fix GameProfileCache concurrency
|
|
+ protected final java.util.concurrent.locks.ReentrantLock stateLock = new java.util.concurrent.locks.ReentrantLock();
|
|
+ protected final java.util.concurrent.locks.ReentrantLock lookupLock = new java.util.concurrent.locks.ReentrantLock();
|
|
+ // Paper end - Fix GameProfileCache concurrency
|
|
+
|
|
public GameProfileCache(GameProfileRepository profileRepository, File cacheFile) {
|
|
this.profileRepository = profileRepository;
|
|
this.file = cacheFile;
|
|
@@ -67,11 +72,13 @@ public class GameProfileCache {
|
|
}
|
|
|
|
private void safeAdd(GameProfileCache.GameProfileInfo entry) {
|
|
+ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
|
|
GameProfile gameprofile = entry.getProfile();
|
|
|
|
entry.setLastAccess(this.getNextOperation());
|
|
this.profilesByName.put(gameprofile.getName().toLowerCase(Locale.ROOT), entry);
|
|
this.profilesByUUID.put(gameprofile.getId(), entry);
|
|
+ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency
|
|
}
|
|
|
|
private static Optional<GameProfile> lookupGameProfile(GameProfileRepository repository, String name) {
|
|
@@ -128,17 +135,20 @@ public class GameProfileCache {
|
|
|
|
// Paper start
|
|
public @Nullable GameProfile getProfileIfCached(String name) {
|
|
+ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
|
|
GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
|
|
if (entry == null) {
|
|
return null;
|
|
}
|
|
entry.setLastAccess(this.getNextOperation());
|
|
return entry.getProfile();
|
|
+ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency
|
|
}
|
|
// Paper end
|
|
|
|
public Optional<GameProfile> get(String name) {
|
|
String s1 = name.toLowerCase(Locale.ROOT);
|
|
+ boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
|
|
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
|
|
boolean flag = false;
|
|
|
|
@@ -154,8 +164,12 @@ public class GameProfileCache {
|
|
if (usercache_usercacheentry != null) {
|
|
usercache_usercacheentry.setLastAccess(this.getNextOperation());
|
|
optional = Optional.of(usercache_usercacheentry.getProfile());
|
|
+ stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency
|
|
} else {
|
|
+ stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency
|
|
+ try { this.lookupLock.lock(); // Paper - Fix GameProfileCache concurrency
|
|
optional = GameProfileCache.lookupGameProfile(this.profileRepository, name); // CraftBukkit - use correct case for offline players
|
|
+ } finally { this.lookupLock.unlock(); } // Paper - Fix GameProfileCache concurrency
|
|
if (optional.isPresent()) {
|
|
this.add((GameProfile) optional.get());
|
|
flag = false;
|
|
@@ -167,6 +181,7 @@ public class GameProfileCache {
|
|
}
|
|
|
|
return optional;
|
|
+ } finally { if (stateLocked) { this.stateLock.unlock(); } } // Paper - Fix GameProfileCache concurrency
|
|
}
|
|
|
|
public CompletableFuture<Optional<GameProfile>> getAsync(String username) {
|
|
@@ -191,6 +206,7 @@ public class GameProfileCache {
|
|
}
|
|
|
|
public Optional<GameProfile> get(UUID uuid) {
|
|
+ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
|
|
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByUUID.get(uuid);
|
|
|
|
if (usercache_usercacheentry == null) {
|
|
@@ -199,6 +215,7 @@ public class GameProfileCache {
|
|
usercache_usercacheentry.setLastAccess(this.getNextOperation());
|
|
return Optional.of(usercache_usercacheentry.getProfile());
|
|
}
|
|
+ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency
|
|
}
|
|
|
|
public void setExecutor(Executor executor) {
|
|
@@ -279,7 +296,7 @@ public class GameProfileCache {
|
|
JsonArray jsonarray = new JsonArray();
|
|
DateFormat dateformat = GameProfileCache.createDateFormat();
|
|
|
|
- this.getTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot
|
|
+ this.listTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot // Paper - Fix GameProfileCache concurrency
|
|
jsonarray.add(GameProfileCache.writeGameProfile(usercache_usercacheentry, dateformat));
|
|
});
|
|
String s = this.gson.toJson(jsonarray);
|
|
@@ -320,8 +337,19 @@ public class GameProfileCache {
|
|
}
|
|
|
|
private Stream<GameProfileCache.GameProfileInfo> getTopMRUProfiles(int limit) {
|
|
- return ImmutableList.copyOf(this.profilesByUUID.values()).stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit((long) limit);
|
|
+ // Paper start - Fix GameProfileCache concurrency
|
|
+ return this.listTopMRUProfiles(limit).stream();
|
|
+ }
|
|
+
|
|
+ private List<GameProfileCache.GameProfileInfo> listTopMRUProfiles(int limit) {
|
|
+ try {
|
|
+ this.stateLock.lock();
|
|
+ return this.profilesByUUID.values().stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit(limit).toList();
|
|
+ } finally {
|
|
+ this.stateLock.unlock();
|
|
+ }
|
|
}
|
|
+ // Paper end - Fix GameProfileCache concurrency
|
|
|
|
private static JsonElement writeGameProfile(GameProfileCache.GameProfileInfo entry, DateFormat dateFormat) {
|
|
JsonObject jsonobject = new JsonObject();
|