A command with multiple redirect modifiers can ignore "maxCommandChainLength"
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If your function has commands with two or more redirect modifiers, the function may or may not ignore maxCommandChainLength and keep running unpredictably depending on the command quota before each of the commands is executed. By carefully adjusting the number of command executions, it is even possible to define a function that recurses infinitely as in https://github.com/intsuc/23w44a_infinite_recursion.
How to reproduce
/gamerule maxCommandChainLength 1
/say !
→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed./execute as @s run say !
→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier./execute as @s as @s run say !
→
A message is printed; this would be an incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed ignoring maxCommandChainLength./execute summon armor_stand summon armor_stand run say !
→
Two armor stands are created and a message is printed; this would be an incorrect behavior. This result indicates that each redirect modifier is executed and decrements the command quota regardless of the current command quota.
Code analysis
public void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }
In net.minecraft.commands.execution.tasks.BuildContexts#execute, ExecutionContext#incrementCost, which decrements commandQuota, is called for each modifier stage with a redirect modifier. During these stage executions, commandQuota is not checked for execution cancellation, which could result in a negative commandQuota.
Created Issue:
"maxCommandChainLength" can be bypassed by multiple modifier stages
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the current execution in that execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled in situations where the command quota becomes negative.How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the
currentexecution inthatexecution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled in situations where the command quota becomes negative.How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is
about to be executed.Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled in situations where the command quota becomes negative.How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled in situations where the command quota becomes negative.How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If you accidentally define and execute a function that calls itself infinitely and the function has commands with two or more redirect modifiers, the function will run forever or stop unpredictably depending on the command quota before each command is executed.
How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If you accidentally define and execute a function that calls itself infinitely and the function has commands with two or more redirect modifiers, the function will run forever or stop unpredictably depending on the command quota before each command is executed.
How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If you accidentally define and execute a function that calls itself infinitely and the function has commands with two or more redirect modifiers, the function will run forever or stop unpredictably depending on the command quota before each command is executed. (Example)
How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If you accidentally define and execute a function that calls itself infinitely and the function has commands with two or more redirect modifiers, the function will run forever or stop unpredictably depending on the command quota before each command is executed. (Example)
How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !
→A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If you accidentally define and execute a function that calls itself infinitely and the function has commands with two or more redirect modifiers, the function will run forever or stop unpredictably depending on the command quota before each of the commands is executed. (Example)
How to reproduce
/gamerule maxCommandChainLength 1
/say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javaUnable to find source-code formatter for language: code panel. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yamlpublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If you
accidentally define and execute a function that calls itself infinitely and the function has commands with two or more redirect modifiers, the function will run forever or stop unpredictably depending on the command quota before each of the commands is executed. (Example)How to reproduce
/gamerule maxCommandChainLength 1
/say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javaUnable to find source-code formatter for language: code panel. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yamlpublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If your function has commands with two or more redirect modifiers, the function may or may not ignore maxCommandChainLength and keep running unpredictably depending on the command quota before each of the commands is executed. By carefully adjusting the number of command executions, it is even possible to define a function that recurses infinitely as in https://github.com/intsuc/23w44a_infinite_recursion.
How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed bypassing maxCommandChainLength.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }In net.minecraft.commands.execution.tasks.BuildContexts#execute, ExecutionContext#incrementCost, which decrements commandQuota, is called for each modifier stage with a redirect modifier. During these stage executions, commandQuota is not checked for execution cancellation, which could result in a negative commandQuota.
"maxCommandChainLength" can be bypassed by multiple modifier stagesA command with multiple redirect modifiers can ignore "maxCommandChainLength"
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If your function has commands with two or more redirect modifiers, the function may or may not ignore maxCommandChainLength and keep running unpredictably depending on the command quota before each of the commands is executed. By carefully adjusting the number of command executions, it is even possible to define a function that recurses infinitely as in https://github.com/intsuc/23w44a_infinite_recursion.
How to reproduce
/gamerule maxCommandChainLength 1/say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed
bypassing maxCommandChainLength.Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }In net.minecraft.commands.execution.tasks.BuildContexts#execute, ExecutionContext#incrementCost, which decrements commandQuota, is called for each modifier stage with a redirect modifier. During these stage executions, commandQuota is not checked for execution cancellation, which could result in a negative commandQuota.
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If your function has commands with two or more redirect modifiers, the function may or may not ignore maxCommandChainLength and keep running unpredictably depending on the command quota before each of the commands is executed. By carefully adjusting the number of command executions, it is even possible to define a function that recurses infinitely as in https://github.com/intsuc/23w44a_infinite_recursion.
How to reproduce
/gamerule maxCommandChainLength 1 /say !→
A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !→
A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !→
A message is printed; this would be an incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed ignoring maxCommandChainLength.
/execute summon armor_stand summon armor_stand run say !→
Two armor stands are created and a message is printed; this would be an incorrect behavior. This result indicates that each redirect modifier is executed and decrements the command quota regardless of the current command quota.
Code analysis
net.minecraft.commands.execution.ExecutionContext.javapublic void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }In net.minecraft.commands.execution.tasks.BuildContexts#execute, ExecutionContext#incrementCost, which decrements commandQuota, is called for each modifier stage with a redirect modifier. During these stage executions, commandQuota is not checked for execution cancellation, which could result in a negative commandQuota.
If MC-265805 is fixed but MC-265772 is not, MC-265772 and integer overflow could be exploited to execute a tail-recursive function indefinitely. As of 23w41a, it is not possible to reproduce this due to out of memory. See MC-265805 for more information.
I have created functions that actually recurse infinitely in 23w44a using MC-265772: 23w44a_infinite_recursion
If
MC-265805is fixed butMC-265772is not,MC-265772and integer overflow could be exploited to execute a tail-recursive function indefinitely. As of 23w41a, it is not possible to reproduce this due to out of memory. SeeMC-265805for more information.I have created functions that actually recurse infinitely in 23w44a using
MC-265772: 23w44a_infinite_recursionHi, could you please provide us with more information about this issue:
1. Expected outcome after running commands from repro steps. Right now after running commands from last repro step we noticed that game spawns two armor stands and do not say 1, is this the correct behavior?
2. How '/gamerule maxCommandChainLenght' commands affect the game and what should it allow/disallow.
This is already fixed. And if you don't know what the game rule does, you can just look it up. A report does not have to explain a feature's behavior, only the expected and actual result (which this does).