Hello, I am crooked.
It’s not that Log4j broke the loopholes. Then a small partner came to me a few days ago: I used Lombok's @Slf4j in my project. Will it have any impact?
.png)
You said it was a coincidence, and I also used this annotation, so I took a look at it at the time.
Let me start with the conclusion: whether it affects or not depends on the log4j2 package that your project depends on, and it has nothing to do with Lombok.
In addition, "I beg you to think twice before asking, don't waste our time, don't ask questions that you can figure out by yourself" is not what I said, but the author of Lombok said:
Why would he say such slightly angry words?
I will show you.
Speaking from issue
If you find the Lombok project on github, and then check its issue, you will see that the log4j issue has been pinned to the top:
So on this issue, I will start with this issue, which has an authoritative answer from the maintainers of Lombok.
https://github.com/projectlombok/lombok/issues/3063
The title of this issue is translated as:
Conclusion: Regarding log4j's 0day problem, Lombok itself is not affected.
Attention, there is a very intriguing word in this: itself.
Generally, we just say that Lombok is not affected. Why do we have to add itself?
There is a story in it.
First, at 5:41 pm on December 10th, the afternoon when the vulnerability was revealed, a buddy raised this issue in the issue of Lombok:
He said: Iron men, something went wrong, log4j broke a huge hole, hurry up and fix it.
Then an old iron named Rawi01 jumped out and said:
Brother, can you explain how this vulnerability affects Lombok? As far as I know, log4j is only needed if there are no compilation errors when running tests.
This old man obviously knows Lombok better. He has actually pointed out the key point: Lombok does not rely on log4j.
But then someone pointed out:
Lombok provides an annotation called @log4j. This thing uses the Log4J library, after all, it needs to be used to record logs.
There is indeed this comment. Let me show you:
I usually use the @Slf4j annotation, but it does provide other log annotations. Click here to leave it out, and I’ll talk about it later.
The main task now is this issue, and then look down.
Next, a man who controls the field came to see what he said:
He first introduced the buddy who said @log4j annotation and explained to him: Although Lombok will generate code to create a logger instance, we do not ship, distribute or require any specific version, if the user wants to use it For this annotation, they need to provide the corresponding dependencies themselves.
Here I have three words that I haven't translated yet: ship, distribute or require.
Require is actually very simple, it is a requirement.
This means that we don't require users to have a dependency on the specified version when they use it. To put it bluntly, if you want to use @Log4j, then provide the corresponding dependency. As for the version of this dependency, Lombok doesn't care.
The remaining ship literally translates to "transport", and "distribute" means "distribute".
Let me explain this with a picture:
As you can see, I referenced Lombok in the project, but it did not transitively depend on any other packages. It's not like spring-boot-starter followed by a big pile of things.
I understand this is what he wants to express: we do not ship, distribute or require any specific version.
Then it goes on to say: You can indeed find a version of log4j2 in our code base, but it is only used in the test code so that the generated code can be compiled without errors.
Finally this buddy concluded: rest assured, old irons, Lombok can still be used safely. But I think even though Log4j is referenced in the test code, the dependencies should be updated to a safe version.
How do you feel that this old man speaks with an unquestionable sense of confidence.
I felt this way after reading this answer, and faintly felt that this was a bigwig.
So I went to pick up the background of this old man:
Originally, I wanted to find the answer from his github homepage, but I didn't find any projects related to Lombok.
And there are only 49 followers, which doesn’t fit the big guy’s data:
So I switched to the browser and searched: Roel Spilker Lombok.
Found this:
Only to find out, good fellow, he is Lombok's father, no wonder he speaks so hard.
I also found a lace news: They also contributed their ideas about Lombok to Oracle before, but they were rejected. Officials did not support it. They had to stand up and do it themselves.
Now Lombok's market share is still very high, and it can be considered a success in doing this.
In addition, in fact, you can see his identity directly here:
Collaborator is a partner. His answer can be equivalent to the official answer.
Up to this point, things are still developing relatively smoothly. The official came to answer this question in person, and it has already stated clearly:
It can be finished.
However, I was afraid of appearing. But, what happened later I felt a bit outrageous.
First, Brother Sunrise Chair gave a kill:
He first quoted the author's reply, and then said that if you declare the dependency of Lombok in your maven or gradle, won't the dependency of Log4j also be included? This may be the only dependency on the classpath, and the code generated by Lombok will also use it? Please correct me if I am wrong...
What does he want to express?
First of all, I think that after reading the author's answer, he felt that Lombok relies on Log4j, so his core question is, if I quote Lombok, will the dependency of Log4j also be passed in?
But the author has said: there is a version to be found in our code base, but that's only used in the test category.
As long as you have a little understanding of the working principle of open source projects, you also know that the test-related parts will not be provided. If you want to see the test category, you have to pull down the project source code.
Simply put, if you rely on Lombok through maven, you will definitely not see test-related things.
Then an old man named RuanNunes also came out to fill the gun and offered a second kill. He said that he found this thing in the entire project:
This configuration is vulnerable.
Then the author responded to these two questions one by one:
First of all, the guy who said Lombok depends on log4j. Listen: Lombok does not depend on log4j or any other libraries. If you use @Log4j annotations in your code, but do not directly or indirectly rely on log4j, your compilation will generate an error message.
Regarding this point, the previous dependency analysis screenshot has also been explained, I will not post it again.
Then let's talk about the buddies who found the vulnerability, listen: there is a text file in the same directory as the vulnerability you found, but when you take a look, you know that we will not include this file as part of our release.
So I understand that it will not be released at all, even if there are loopholes, there is nothing wrong with Lombok users, right?
I also went to find this file the author said, which is:
https://github.com/projectlombok/lombok/blob/d3b763f9dab4a46e88ff10bc2132fb6f12fda639/src/support/info.txt
This document clearly says: not part of lombok itself.
If the first two questions have made the author a little uncomfortable, then the next question may be the tipping point, complete the three kills, and take it away in one wave:
This buddy came up and said: Lao Tie, let me ask a question. I use the @slf4j annotation. Does this vulnerability have anything to do with me?
The blood pressure is up.
I think this buddy didn't read the previous reply at all before asking the question. If he asked this question before the author explained it twice, then I think it is completely understandable.
But under the premise that the author has explained "Lombok is not affected" twice in this issue, he slammed it, and it came up as a crit:
Brother, is there a problem with this annotation I used?
As I write, blood pressure is up a little bit.
This is especially like a scene where I was discussing transaction failure with others in a group before. The discussion has come to an end. Suddenly a buddy jumped out and a code snippet was thrown out. The writing of this snippet is caused by a classic this call. The scenario where transaction annotations fail, and this is part of what we just discussed.
Knowing that we are discussing this issue, even if you flip up a bit, wouldn’t it be better to understand the context and ask questions?
Let's not go far, maybe I over-interpreted it. Anyway, I think everyone should study the art of asking questions.
Back to our main storyline, let’s see how the author answered this question:
He said: Lao Tie, I can't help you with this problem. I don't know anything about this vulnerability, and I don't even understand why you think it will also affect @Slf4j.
What I can tell you is that Lombok does not use, deliver, or require dependencies on these libraries.
Our working principle is to generate your "invisible source code".
If you have any reason to suspect that similar problems may also occur in their products, please contact the maintainer of Slf4j.
I don’t know if it’s my personal feeling. I think the author was a little angry when he responded to this question: What are these problems? I have already replied twice before and are not affected? How do I feel that my project users do not understand the basic principles of Lombok?
After the author responded to the reckless questioner, the questioner replied politely:
Thanks to Lao Tie, I checked, I am using SpringBoot's default logback.
The author also pinned this issue to the top and revised the title of this issue.
So, after some interpretation, now you look at this title:
Why does the author emphasize "itself", because Lombok does provide a log function, but as for what package to refer to and which version of the package, it has nothing to do with Lombok. Lombok itself (itself) is safe.
Finally, the author gave the log4j vulnerability to Lombok's Latest assessment (latest assessment), which is a concluding statement:
Translate the key things for everyone.
This vulnerability only exists in the Log4j code package below version 2.16.0, and does not exist in any other logging framework.
Lombok does not transitively depend on any Log4j package, nor does it declare a dependency on anything.
If you use any Lombok annotations, such as @Log4j, Lombok will generate code that uses these libraries, but your project must include dependencies on these libraries, otherwise the code generated by Lombok will not be compiled.
Similarly, you are responsible for owning these packages in your runtime, otherwise the initialization of the class may fail.
In the Lombok test code, we used to have a version containing this vulnerability, but because the test does not process any user input (the test is hard-coded), and the generated code is not even executed, running the test did not result in the execution of the test machine RCE (Remote Code/Command Execution Vulnerability) appeared on.
So, old folks, Lombok itself does not need to make any changes, nor does it take any responsibility for the safety of your project. After all, the package was not introduced by us.
If you disagree with the current assessment, please add a comment on this question.
But please make sure you have read the other comments and make sure you understand the question.
The last two sentences, taken out separately, I really like these two sentences:
Please think twice before asking, don't waste our time, don't ask questions that you can figure out by yourself.
If you think we have missed something, or have new information, please say it out loud.
Then, you notice that the subtitle used by the author here is: The balancing act.
It translates to "balanced behavior", what the hell?
NO,NO,NO:
A little slang for everyone, you are welcome.
Supplement
The main storyline is over, now let me add a few more explanations.
Let me talk about the notes on the log that were not shown in the previous section.
In fact, there are a lot of comments about logs in Lombok. You can directly read the official documents:
https://projectlombok.org/features/log
With so many annotations, it's not interesting to talk about them one by one. I will choose @Slf4j and @Log4j2 for a demonstration.
First of all, we can build a pure SpringBoot project that only contains these two dependencies:
At this time, if I don’t move anything, just change the startup class a bit:
Then in order to eliminate interference items, I adjusted the log printing level to Error:
logging.level.root=error
At the same time turn off the banner output:
spring.main.banner-mode=off
The banner is this stuff:
Start the project at this time, the log output is like this:
You can see that the log we use at this time is logback, the reason I have also talked about in the previous article, because the default log implementation used by Springboot is logback.
This can also be seen from the project dependencies:
In addition, please pay attention, I specially cut out the import part. Except for @Slf4j annotations, there are no log-related annotations here.
Then, I will pay attention to the class file compiled at this time:
Automatically introduce slf4j related packages, and then generate this line of code:
private static final Logger log = LoggerFactory.getLogger(LogdemoApplication.class);
At this time, I don’t know if you have thought about annotation-related things at compile time, but don’t panic.
Come, I ask you: Why can it introduce slf4j related packages?
Because I depend on it:
Well, if I remove the core dependency of logback at this time, what will happen, do you think it will not compile?
It won't compile, but because the Slf4j package is still there, it's just a log facade.
However, an exception will be thrown when running, because the specific implementation class related to the log cannot be found:
Then, what if I want to use log4j2 log implementation?
I also wrote in the previous article:
Remove the default dependency of Springboot, and then import the Log4j2 package.
At this time, the project dependency graph is like this, you can see that there is no logback-core, only log4j-core:
Run the project again, the log implementation becomes Log4j:
Did you find out? Except I moved the pom dependency, no other line of code changed. The log framework changed from logback to log4j.
And there is no change in the class file, so I won't take a screenshot.
This is the credit of Slf4j, this is the meaning of "facade", and this is why it is recommended that you use Slf4j in your projects instead of specific log implementations such as logback and log4j.
Next, let's talk about the annotation @Log4j2.
We still restore the dependency to its original pure state, which is like this:
Then we changed the annotation to @Log4j2, but the Log4j-core package was not introduced in our project at this time, so do you think there will be a problem?
There will be no problem, we can take a look.
First look at the output:
The log implementation class at this time is SLF4JLogger.
Where did this stuff come from?
Take a look at the class file:
These two classes are from the log4j-api package. At the same time, due to the existence of the log4j-to-slf4j package, the final implementation class is bridged to SLF4JLogger:
If I remove the log4j-api package, do you think it will not compile?
It must not compile, because the package does not exist anymore, and the class file cannot be produced:
If I don't want to use the SLF4JLogger class, I want to use real log4j.
Simple, bring in the dependency of log4j:
Well, I said so much nonsense. I will take the trouble to exclude and introduce log-related packages for you, and show you the output, and the whole process does not involve changes in the Lombok package, all in order to confirm these two again. Sentence:
If you use any Lombok annotations, such as @Log4j, Lombok will generate code that uses these libraries, but your project must include dependencies on these libraries, otherwise the code generated by Lombok will not be compiled.
For example, I removed the log4j-api package earlier, did it fail to compile?
Similarly, you are responsible for owning these packages in your runtime, otherwise the initialization of the class may fail.
For example, I removed the logback-core package earlier. There was no problem when compiling, but when the service was running, did it throw an exception that the class could not be found?
Is it to prove again:
Talk about the principle
Earlier I mentioned a sentence of "compilation time annotation", I don't know if you don't understand this stuff.
In fact, it's normal if we don't understand, because we rarely customize compile-time annotations when we write business code. It's almost the same if we have a runtime annotation.
The core working principle of Lombok is compile-time annotation.
In fact, I don't know in-depth, I just know how it works, and I haven't studied the source code in depth.
But I can share with you two things to pay attention to and where to go to learn about this stuff.
The first thing to note is here:
The source code of log related comments is in this section. It is very strange to see that these files end with SCL.lombok. What is this?
This is lombok's careful thinking. In fact, these are all class files, but in order to avoid polluting user projects, it has done special treatment.
So when you open this type of file, just choose to open it as a class file, and you can see the specific content inside.
For example, you can look at this file:
lombok.core.handlers.LoggingFramework
You will find that you are like an enumeration, and have written a lot of log implementations:
In this, the log that each annotation needs to be generated is hard-coded. It is precisely because of this that Lombok knows what log annotations you use and what kind of log should be generated for you.
For example, log4j looks like this:
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(TargetType.class);
This can also correspond to our previous class file:
And SLF4J is like this:
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TargetType.class);
The second thing to pay attention to is to find the entrance:
The entry point for loading these class files is in this place, which is based on the Java-based SPI mechanism:
There are two lines of static inner classes in AnnotationProcessorHider. Let’s look at one of them, AnnotationProcessor, which is an abstract class inherited from AbstractProcessor:
javax.annotation.processing.AbstractProcessor
This abstract class is the entrance in the entrance, the core in the core.
In this entry, a class loader is initialized, called ShadowClassLoader:
What it does is to load the class files that are marked as SCL.lombok.
Then how do I know that Lombok is based on compile-time annotations?
In fact, this thing is written in the two books I have read, and I have a vague impression. When I was writing the article, I turned it out and read it again.
The first is the fourth part of "In-depth Understanding of Java Virtual Machine (Third Edition)" Chapter 10: Front-end Compilation and Optimization Section of Program Compilation and Code Optimization.
There is a section dedicated to inserting annotations:
The main work site of Lombok is in the process of javac compilation.
On page 361 in the book, several stages of the compilation process are mentioned.
From the overall structure of Java code, the compilation process can be roughly divided into a preparation process and three processing processes:
- 1. Preparation process: Initialize the plug-in annotation processor.
2. The process of parsing and filling the symbol table, including:
- Lexical and grammatical analysis. The character stream of the source code is transformed into a set of tags, and an abstract syntax tree is constructed.
- Fill the symbol table. Generate symbol address and symbol information.
- 3. The annotation processing process of the plug-in annotation processor: During the execution phase of the plug-in annotation processor, the actual part of this chapter will design a plug-in annotation processor to affect the compilation behavior of Javac.
4. Analysis and bytecode generation process, including:
- Mark check. Check the static information of the grammar.
- Data flow and control flow analysis. Check the dynamic running process of the program.
- Decipher syntactic sugar. Reduce the syntactic sugar of simplified code writing to its original form. (Syntax sugar in java includes generics, variable length parameters, automatic loading and unboxing, traversal loop foreach, etc. The JVM runtime does not support these syntaxes, so it needs to be restored during the compilation stage.)
- Bytecode generation. Convert the information generated in the previous steps into bytecode.
If the process of javac compilation is Lombok's work site, then the "annotation processing process of the plug-in annotation processor" is its workstation.
The book also mentions the working principle of Lombok:
.png)
The second book is "In-Depth Understanding of JVM Bytecode". In its Chapter 8, it also describes in detail the processing principles of plug-in annotations, which also mentions Lombok:
.png)
Finally, I drew a schematic diagram like this:
.png)
If you understand the description of the first dozen pages in the book, then this picture will be clearer.
In short, the core principle of Lombok is to make a magical change to the class file during the compile period, which will help you generate a lot of code.
This is also what the author mentioned:
invisible source code, invisible source code.
The invisible here refers to the invisible in the java file, it still has nowhere to hide in the class file.
If you are interested in understanding its principles in depth, you can read the two books I mentioned earlier, which have hands-on practical development.
I won't write it. One reason is that the threshold is indeed high, and the writing is jerky and difficult to understand. The other reason is not because I am lazy.
This article has been included in the personal blog, everyone is welcome to play:
https://www.whywhy.vip/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。