Pourquoi je soutiens Benoît Hamon

01 mars 2017

Tags: politique ps hamon

Cette élection présidentielle est unique. A bien des égards. La première raison, qui m’est insupportable, est que pour la première fois, une candidate FN, Marine Le Pen, est en position de gagner cette élection: un mirroir de ce qui se passe à l'échelle mondiale, avec un repli sur soi et une montée inquiétante du populisme. La seconde, parce qu'évidemment jamais le rejet des politiques en place n’aura été aussi important. Pour autant, c’est aussi une opportunité, pour une fois, de changer radicalement de société. Les choix qui s’offrent à nous sont particulièrement clairs: une droite extrêmement dure avec Fillon et Le Pen, ou une gauche sans compromis avec Hamon ou Mélenchon. Sans oublier, bien sûr, l’outsider désormais favori Macron, dont je ne sais toujours pas s’il a des convictions ou s’il est simplement le reflet de la nature, qui a horreur du vide (je ne sais pas où voter, allons là où tout le monde semble se rallier).

Pour ma part, je vote par conviction. J’aurais bientôt 38 ans, et je dois dire que jamais, je n’ai été si proche des idées d’un candidat. Je le suis depuis plusieurs mois, bien avant la primaire, et mes amis proches savent que je leur en parlais et que je croyais en sa victoire. Dans ce billet, je veux vous donner quelques pistes pour lesquelles je soutiens Benoît Hamon, et pourquoi je vous invite à le rejoindre.

Note
A mes lecteurs habituels, ce billet n’a rien à voir avec mes interventions techniques. Libre à vous de l’ignorer. Par ailleurs, je souligne évidemment que ce qui est écrit ici n’engage que moi, et personne d’autre que moi.

Le revenu universel

Commençons par le revenu universel, parce qu’il est le plus clivant. Même pendant la primaire des gauches, j’ai entendu des absurdités incroyables à son sujet, ou des questions très orientées des journalites simplement destinées à décridibiliser cette proposition. Pourtant, c’est une idée neuve, défendue par de nombreux économistes, de droite comme de gauche, ainsi que d'éminents chefs d’entreprise, tels Elon Musk, le fondateur Tesla, Space X, devenu richissime avec la revente de PayPal, … Pas spécialement un exemple de ratage. Rappelons son principe de base: donner, à chaque citoyen, un revenu identique (dont le montant varie selon les propositions), universel, indépendemment de ses resources, à compter de sa majorité. A droite, on crie alors à l’assistanat. A gauche, on hurle qu’il ne faut pas donner aux riches qui ont déja bien assez.

Mais le monde change. Le travail change. Quiconque vous dit le contraire est soit aveuglé, soit vous ment. Je travaille dans l’informatique, et par mon activité professionnelle qui m’amène à rencontrer de nombreux développeurs du monde entier, je pense être assez bien placé pour voir que ce je développe, ce qui arrive, est une nouvelle révolution industrielle (mais pas seulement, les services sont aussi touchés).

En France, vous aurez déja remarqué que les caissières se font moins nombreuses, qu’il n’y a plus personne aux péages, que le métro n’a plus de conducteur, qu’on nous vend du pain frais dans un distributeur… Mais nous sommes très en retard.

Quoi qu’on en pense, à moyen terme, la révolution robotique est en marche, et c’est une bonne chose. Bien sûr, on ne parle pas d’un remplacement à court terme. Tout celà est progressif. Des métiers vont disparaître, d’autres se transformer, d’autres encore vont apparaître, mais il est indéniable que le volume de travail global disponible à l’humain fond comme neige : nous sommes toujours plus nombreux sur la planète, le PIB augmente, pour autant le volume d’heures travaillées évolue peu. L'étude la plus optimiste de l’OCDE indique que près de 10% des emplois sont menacés à court terme. Une autre étude parle elle de 50% des emplois remplaçables par des robots à l’horizon 2030. Lorsqu’on choisit une politique, il ne faut pas penser à court terme, mais à nos enfants. Même s’il est idiot d’affirmer que le travail va disparaitre (qui peut sérieusement le croire ?), il se transforme, et il faut embrasser cette transformation.

J’en suis convaincu, les politiques qui ignorent ces changements vivent dans un monde qui n’existe plus: ils achètent leur pain au chocolat à 10 centimes, font leurs courses chez Prisunic et rêvent de payer leurs assistants parlementaires européens en Francs. Ignorer les changements du travail, c’est ignorer l’avenir, s’aliéner à ceux qui embrassent ce changement, perdre notre souveraineté. Au lieu de combattre ce futur, il faut au contraire s’y préparer et repenser notre société autour de ces changements. L’automatisation, la robotisation, n’engendre pas de perte de valeur: celle-ci est toujours présente, les prix ne baissent en général pas: la productivité augmente et on réduit la pénibilité. Deux choix de société s’engagent alors: profiter de cette amélioration pour vivre mieux, ou une fuite en avant productiviste. A ce sujet, je conseille la lecture de ce billet à mes lecteurs anglophones: ou comment le temps de travail est fonction des évolutions de la société, pas une constante.

Ceci appelle à une révolution fiscale: là où aujourd’hui on taxe le travail, il faudra demain taxer les robots, comme le propose aussi Bill Gates. Si ce terme peut faire sourire, il cache en réalité de nombreuses ramifications: évasion fiscale en particulier. Quelle pire injustice que de voir nos petites entreprises crouler sous des 30% de taxes, lorsque les grandes entreprises étrangères qu’on utilise tous (smartphones, distribution), qui robotisent, automatisent et n’embauchent quasiment pas, placent "leur" argent dans des paradis fiscaux et ne paient pas d’impôts, grâce à des montages financiers complexes avec la complicité de nos cabinets comptables ? Ne marchons nous pas sur la tête ?

Alors, oui, on peut continuer à sourire, dire qu’il faut baisser les charges pour les entreprises et les salariés (ce qui serait une bonne chose), mais si on n’embrasse pas cette virtualisation de l'économie, on ne fait que réduire l’assiète des cotisations, et se ruiner un peu plus.

Alors que vient faire le revenu universel là dedans ? Il permet:

  • de mettre fin à la grande pauvreté, en particulier en remplaçant le RSA, belle idée mais tellement complexe que nombreux sont ceux qui y ont droit et ne le demandent pas. Par ailleurs, si le revenu est effectivement universel, l’argument de la fraude aux aides ne tient plus: tout le monde y a droit, point.

  • de favoriser le temps partiel. Dans certains Etats, comme les Pays-Bas, c’est déja la norme, y compris chez les hommes, réduisant ainsi les inégalités homme-femme. Le PIB de ce pays n’a pas chuté, les employés n’en sont que plus heureux.

  • de maintenir l’emploi. Des histoires d’agriculteurs qui travaillent comme des fous et ne s’en sortent plus, on en lit tous les jours. Avec 350€ par mois, franchement, vous resteriez? Je connais personnellement l’un d’entre eux qui a fini par abandonner. Avec le revenu universel, on permet à ces travailleurs pauvres de rester en poste.

  • de choisir de travailler plutôt que rester à la maison. Je ne sais pas pour vous, mais le nombre de fois où j’ai entendu quelqu’un me dire "avec mes frais de garde et de transport, ça me revient plus cher d’aller travailler que de rester chez moi". Pas franchement encourageant, non?

  • de valoriser des activités non commerciales mais importantes pour la société : travail associatif, aides à la personne, …

  • de partager le travail: à coût égal pour une entreprise, il sera possible de prendre 2 salariés au lieu d’un seul, et ainsi donc réduire les risques de TMS (pour les travaux manuels) ou burn-outs : il existe une limite physique à la productivité.

En revanche, le RU ne rend pas les riches plus riches. C’est un revenu. En conséquence, il est imposable. Il s’ajoute donc à votre salaire et suivant vos revenus, vous devrez payer ou non des impôts dessus. Il est donc incorrect de dire que ça coute 400 milliards (ou plus). En pratique, il coûte sensiblement moins, en particulier si son implémentation se fait via un crédit d’impôt. La justice fiscale est maintenue. Le soumettre à conditions de ressources, c’est augmenter son coût intrinsèque (gestion, contrôles) ou inciter à la fraude pour quelque chose qui ne change au final rien. Quant à l’argument de l’incitation à l’oisiveté, il me fait doucement sourire. A tous ceux qui me l’ont avancé, je leur ai posé une question simple: si on vous donnait 600€ par mois, est-ce que vous quitteriez votre boulot ? Est-ce que ça vous suffirait ? Je pense que je n’ai pas besoin de vous donner les réponses…

Alors, certains taxent cette proposition d’irréaliste, utopique, irréalisable et j’en passe. Ils doutent de sa crédibilité. Mais que proposent-ils ? Toujours les mêmes solutions ! Réduire les charges des entreprises (merci Fillon pour son exonération sur les bas salaires, effet d’aubaine pour n’augmenter personne, alors que la répartition de la valeur n’a jamais été aussi dévaforable aux pauvres et classes moyennes). Augmenter la TVA (belle justice fiscale !). Qui se souvient, à l’inverse, de la baisse de la TVA pour la restauration, en l'échange, soit disant, d’emplois et de baisses de prix ? Qui se souvient encore du badge 1 million d’emplois de Pierre Gattaz en l'échange du pacte de compétitivité ? Travailler plus, en défiscalisant les heures supplémentaires, encore une mesure populaire, mais qui ne crée pas d’emplois (la vraie injustice), et n’est qu’un pansement sur une jambe de bois: est-ce que cette politique a déja une seule fois marché ?

Autre question: qui peut encore croire que repousser l'âge de la retraite est une solution, alors même que les seniors sont "trop vieux pour travailler" dès 50 ans ? Je ne suis fondamentalement pas contre repousser l'âge de la retraite (qui finalemement va de pair avec l’augmentation de l’espérance de vie), mais faire ça alors même que nos seniors sont au chômage n’a aucun sens. Il faut d’abord revaloriser l’expérience, changer notre vision. Comment peut-on imaginer créer des emplois en faisant travailler plus ceux qui ont la chance d’en avoir un, quitte à les briser ?

60 milliards ont été engloutis dans le CICE, pour quel résultat ? Autre solution classique, se reposer sur une croissance, dont les prévisions sont systématiquement revues à la baisse, et donc les conséquences sur notre planète commencent sérieusement à se faire sentir ? Ou faciliter le licenciement, en croyant dogmatiquement que ça facilite l’embauche ? Ce dont ont besoin les entreprises, ce n’est pas de pouvoir licencier. Personne ne veut licencier par plaisir. Ce qu’il faut, c’est de la stabilité. Connaitre les règles du jeu à l’avance, et qu’elles ne changent pas tous les jours, ainsi qu’une juste concurrence : que les petites paient les mêmes impôts que les grosses, et que les différences de législation entre partenaires commerciaux ne soient pas un frein aux entreprises locales (donc, un protectionnisme raisonné). Alors, dites-moi, qui est irréaliste et utopique ?

Alors, on va nous parler de "valeur travail". Ou la réalisation par le travail. Est-ce là une valeur que je veux transmettre à mes enfants ? Non. Si le travail est important, il ne doit pas être le critère de réalisation, en particulier dans une société ou le travail se raréfie. C’en est de plus insultant pour tous ceux qui ne trouvent pas d’emploi, ou pour ceux qui s’investissent autrement (associations, sportifs, artistes) et contribuent à la grandeur de notre pays. Ce que j’enseigne à mes enfants, c’est qu’il faut se donner à fond et ne pas se fermer de portes. Je leur apprends la tolérance. Je leur apprends à apprécier la chance qu’ils ont par rapport à d’autres. Je leur apprends que l’effort est récompensé, mais que parfois, la vie est injuste. Travailler, oui, mais choisir. Le choix, la liberté sont la clé de la réalisation.

Et d’avenir, parlons-en.

La révolution écologique

Lorsqu’on parle de révolution industrielle, on ne peut ignorer la transition écologique à mener, et son potentiel énorme en termes d’emplois. La France a trop longtemps dirigé tous ses crédits vers le nucléaire, en en ignorant volontairement le coût réel (démantèlement, notamment) et son impact sur l’environnement (bien sûr, un accident n’arrive jamais). Mais le plus gros mensonge, c’est encore notre fameuse indépendence énergétique. Faut-il rappeler d’où vient le combustible si précieux dont nous avons besoin ? Pour autant, il ne faut pas être naif: sortir du nucléaire prend du temps, mais c’est aussi une énorme opportunité. En échelonnant dans le temps, comme le propose Benoît Hamon, on permet de maintenir notre capacité de production, tout en ouvrant de nouvelles voies de développement, créant des emplois. La catastrophe serait de faire comme en Allemagne, où toutes les centrales ont été remplacées par des centrales à charbon, dont le bilan carbone est redoutable… Cette carte interactive est plus que parlante…

Mais l'écologie ne se limite pas au nucléaire. L’opposition ferme de Benoît Hamon aux perturbateurs endocriniens est un autre exemple de ce que j’aime dans sa vision: il est temps d’en finir avec la dictature de la croissance, qui détruit notre environnement et provoque de graves maladies. Penser que je puisse être responsable du futur cancer de mes enfants m’est juste impossible: si je peux faire quelque chose aujourd’hui, quitte à sacrifier un peu de confort (je suis le premier à changer de smartphone tous les 3/4 ans, mais en ai-je vraiment besoin ?), faisons-le. Il existe des solutions: circuits courts, production raisonnée, retour à une agriculture prenant en compte les spécificités régionales, réduction de notre consommation de viande, … Nous devons aussi nous responsabiliser en tant que consommateurs: acheter toujours moins cher, c’est inciter à la baisse des salaires ou à la délocalisation.

La République

Un des derniers points que je souhaite discuter ici est ma vision de la République. Tout d’abord, je souhaite le retour au septennat. Je pense que le passage au quinquénat a été une catastrophe, contribuant à la peopolisation de la politique: on ne cherche plus à faire un projet, on parle tout de suite de la prochaine élection. Même si je n’ai pas d’idée précise de ce que doit être la prochaine République, il me semble clair que celle-ci est à bout de souffle. Conçue pour le Général de Gaulle, l’homme providentiel. Je pense qu’il faut revoir le rôle de Président, et qu’il ne soit plus qu’un garant de nos institutions. Pour celà, il nous faut une assemblée consistuante. Benoît Hamon n’en parle pas spécialemement, et c’est peut-être un des points où je suis plus en accord avec un Mélenchon que lui: il faut bien des points de désaccord. En particulier, je trouve l’idée du 49-3 citoyen avec seulement 1% du corps électoral potentiellement dangereuse : la Manif pour Tous aurait pu bloquer le mariage homosexuel (oui, je suis de ceux qui pensent que donner des droits égaux à tous les citoyens n’est pas supprimer vos propres droits), ou, du temps de Mitterand, je doute que la peine de mort aurait pu être abolie. En clair, je suis plutôt pour une proportionnelle intégrale, répondant à une crise de représentativité qu’entretient élégamment le FN. Mais je suis aussi surtout pour la stochocratie, où le fait de tirer au sort des représentants qui, eux, éliront ceux qui nous gouvernent.

Laïcité et peur de l’autre

Une de mes plus grandes désillusions de l'ère Hollande. C’est là que le Parti Socialiste m’a perdu. J’ai été affligé par la Loi Renseignement. Non seulement elle est liberticide, mais elle est aussi dangereuse pour nos entreprises, réduisant considérablement les sécurités et garanties qu’elles peuvent mettre en place pour leurs clients. Mais le plus grave, le divorce, ce fut la déchéance de nationalité. Sous prétexte de protéger nos concitoyens, nous créons deux catégories de personnes, ce qui est le contraire même des fondements de notre République. Plus encore, une trahison des valeurs de gauche. Aux élections qui ont suivi, pour la première fois de ma vie, je n’ai pas voté PS. Fort heureusement, cette mesure n’est jamais passé, j’aurais eu honte, franchement.

En ce qui concerne le débat sur la laïcité, là encore, je suis à 100% derrière la position de Benoît Hamon: la loi 1901 et rien que la loi 1901. En clair, il s’agit de respecter la liberté de culte. Et respecter toutes les religions. Point. Il faut être sacrément culotté (ou aigri), pour dire "Benoît Hamon est en résonance avec une frange islamo-gauchiste". Malek Boutih, je vous le dis sincèrement, cest propos sont une honte pour la gauche. Les simples sous-entendus de cette phrase me dégoutent. Pire, vous avez donné des arguments à l’extrème droite, que nous combattons de tout notre coeur. C’est indigne et tellement typique de cette gauche archaïque (un paradoxe quand vous vous qualifiez de progressiste) et loin de ses valeurs. Après de telles sorties, ne soyez plus surpris que les gens votent FN et vous bottent les fesses aux élections.

Non, je crois aux valeurs humaines. Le FN tente de nous faire croire que 2000 migrants sont responsables du chômage en France, par pitié, non. Pire encore, les migrants violent nos femmes et mangent nos enfants. Et ils n’avaient qu'à rester chez eux, ou apprendre à nager. Nom d’un chien, où est passé le coeur de la France ? Doit-on laisser mourrir tous ces gens, les suspecter des pires crimes, ou être à la hauteur de notre histoire, nous, le pays des Droits de l’Homme ? Se dire que l’allemagne accueille des millions de migrants dans le même temps où l’on fait des procès à Cédric Hérrou. Dois-je vous rappeler ce que la stigmatisation nous a apporté voici 80 ans ? Ce monde tourne sur la tête…

Les cas Macron / Mélenchon

Pour terminer, parlons rapidement d’Emmanuel Macron et Jean-Luc Mélenchon. A la lecture de ce billet, certains peuvent se dire que je pourrais choisir Mélenchon. C’est vrai, lui et Hamon partagent beaucoup d’idées, tout comme les Verts et à ce titre je suis dépité qu’un accord entre les 3 n’ait pas eu lieu. C'était, à mon humble avis, une opportunité unique pour la gauche compte-tenu de la conjecture actuelle. Je pense, pour ma part, que Benoît Hamon a véritablement compris la transition économique qui nous attend et a une personnalité moins clivante. Les solutions de repli sur soi ne fonctionneront pas. Pas plus que les solutions de Macron, qui reste une énigme. Ses discours sont d’une platitude déconcertante, ses solutions en matière d'économie sont peu ou prou les mêmes que celles de Fillon (libéralisation à outrance du marché du travail), et pourtant… il monte, il monte… Le ralliement de François Bayrou, celui là même qui disait, il y a quelques semaines, que Macron était piloté par les banquiers (ce qui est toujours possible puisqu’il refuse d’indiquer qui le finance)… Je peux me tromper, mais je ne crois pas non plus à la fin du clivage droite-gauche. Nos idées sont différentes. Nos visions de la société sont différentes. Progresser, c’est choisir une vision, et y aller. Faire cohabiter des idées si différentes au sein d’un même gouvernement ne peut conduire qu'à prendre de "petites" décisions ne frustrant personne. Soyez rassurés, cependant, je ne mets pas Emmanuel Macron au même niveau que François Fillon ou Marine Le Pen…

En conclusion, je ne suis pas crédule non plus, tout ceci n’est pas réalisable en un jour et c’est pour celà je j’aime l’approche de Benoît Hamon: jamais il ne s’est présenté comme le candidat providentiel, qui a la réponse à tout. Il faut laisser le temps à une politique de se mettre en place (d’où le septennat). Hamon est un homme qui a une vision, il a travaillé ses dossiers, mais c’est aussi un team player. Toutes ses mesures sont réfléchies, travaillées, et vont dans le sens d’un projet à long terme: un avenir désirable.

La société que l’on souhaite pour nos enfants. C’est à eux que je pense en le choisissant. Ce dont ils ont besoin, c’est d’espoir. Je ne veux pas qu’ils grandissent dans ce monde qu’on nous promet:

Et vous ?

Comments

Gradle and Kotlin, a personal perspective

22 mai 2016

Tags: jvm groovy gradle kotlin

Gradle embraces Kotlin, what about Groovy?

First of all, it’s been a long time since I last blogged, and I’d like to remind that everything written here are opinions of my own and not the views of my employer, which happens to be Gradle Inc as write those lines.

A few days ago, Gradle and Jetbrains announced a partnership to make Kotlin a first class language for Gradle builds, both for build scripts and plugins. Most likely, you know Gradle has been using Groovy since its inception. Lots of people think that Gradle is written in Groovy, which is actually wrong. Most of Gradle is written in Java. The builds scripts are written in Groovy, lots of plugins are written in Groovy, our test cases are written in Groovy (using the best testing framework out there, Spock), but Gradle itself is written in Java.

From my perspective, this is situation has been very disturbing and continues to be so. I have very good friends in the Groovy community, and this move has been seen by some of them as a betrayal. As an Apache Groovy committer, and someone who spent almost 4 years full time implementing new features of the language, most importantly its static compiler, seeing Kotlin promoted as the language of choice for Gradle’s future, it’s a little strange. One could legitimely say, WTF? I’ve been aware of this work for several months now, and my colleagues Rodrigo B. de Oliveira and Chris Beams have done an amazing job in a very short period of time. From a long time Groovy user and Groovy developer point of view, it’s hard not to make this move an emotional thing. However, business is not about emotions. In particular, what are we trying to acheive with Gradle? We’re trying to help developers build their applications. We’re trying to make this elegant, reproducible, scalable and fast. We’re language agnostic. We can build Java, Groovy, Scala, Kotlin, C++, Python, … Gradle has never been the tool to build Groovy applications: it’s been a tool to build software. It’s a tool about automation. And I’ve been complaining enough about communities that build their own tool for their very specific language to understand that this is super important: Gradle is (or aims at) the best tool for building any kind of software. In short, we must think in terms of what is best for our users, and sometimes, this means changing technogies. A product should not be bound to a technology, but a company should even less be bound to it. And given the response that we had after the announcement, supporting Kotlin seem to drive a lot of excitement around Gradle, and that’s a very good thing. So, let’s take that out, and think what it means for Groovy.

Groovy support is not abandoned

First of all, I already said it several times, but better continue to spread the message: support for Groovy in Gradle is not deprecated nor removed. You can still write your scripts in Groovy, you can write your plugins in Groovy, and you will still be able to do it. But Gradle will likely encourage users to migrate to Kotlin. To be clear, Kotlin support is incubating, and there’s a lot to do to make it as usable as the Groovy version. Second, there are tens of thousands of builds written using Groovy, hundreds of plugins written in Groovy, so it’s not tomorrow that Kotlin is going to replace Groovy. However, we care about the future, so we need to think about what it means in the long term. Should we be excited about supporting Kotlin? Yes we should, because Kotlin is an amazing language. Should we continue to be excited about Groovy? Of course we should, because it’s also an amazing language. But it’s old and as such brings a lot of legacy with it. As someone who implemented the static compiler for Groovy, I know it very well. There are things that are hard to change, because a large part of the Groovy community is very fond of its dynamic behavior.

So let’s focus on the two major aspects that led to embracing Kotlin in Gradle. The fist one, and principal, is IDE support. Let’s face it: even before I joined Gradle, when I was giving talks about it, people were complaining about IDE support. Compared to a tool like Maven, supporting Gradle build scripts is complicated. Supporting XML is easy (to some extent). Supporting a dynamic DSL is not. Some say it’s Groovy’s fault, and I want to correct this statement right now: it’s not Groovy’s fault. While Groovy let’s you design dynamic DSLs, the design of the DSL can be changed to make it easier for tools to "discover" things. But when Gradle was designed, there wasn’t any statically compiled Groovy. The idiomatic way to write DSLs in Groovy, at that time, was to heavily rely on runtime metaprogramming. While loving metaprogramming, I’ve always prefered compile time metaprogramming over runtime metaprogramming. For multiple reasons:

  • because in most cases, what you want to do at runtime can be done in a unique, setup phase. For example, create your metaclasses, enrich existing types, configure property missing, method missing, … If it’s setup, it’s better done at compile time, because you can report errors, and because it gives higher performance. This led the way I designed the static compiler, and more features of Groovy after that (traits, type checking extensions, …) : describe what you want to do at compile time.

  • because it makes the life of tools easier. While IntelliJ or Eclipse support DSL descriptors that help them provide completion, those are hard to implement, and often inaccurate. They can only approximate what is going to happen at runtime. And in the end, you’re doing the same job twice: you’re writing a runtime for your DSL, which is dynamic, then you need to write a DSL descriptor for the IDE to understand it. Wouldn’t it be better if all was done in a unique place? Something that both the compiler and the IDE can understand?

So while we know we can describe dynamic Groovy DSLs so that they are understood by IDEs, it’s effectively a lot of work. And if you want to support multiple IDEs, it’s even more work. But in the case of Gradle, it’s even worse: each plugin can provide it’s own "micro DSL". While there’s an "idiomatic" way to configure Gradle builds, it’s no single rule. One can implement it’s own Groovy DSL within the Gradle build. And no luck the IDE would ever understand it. Another pain point is that Gradle adds complexity to complexity in terms of DSL capabilities. For example, when you have a build script that has:

dependencies {
   compile libraries.groovy
}

greeter {
   message = 'hello'
}

sign {
   signature = top
}

often people do not realize that: - dependencies is found on a Project instance - libraries is a user declared variable, that can be found in a plugin, another build script, a project properties file, … (how does the IDE find about it?) - greeter is a convention object, defined by a plugin, to configure the default values of its task - sign is a task, which has a signature property, and top references an extension property from the project

So while this build script is simple to read, it’s hard to understand how it effectively works, because objects can be found at different places, can be provided by different providers (plugins, properties, extensions), but everything is accessed using a single notation. This is bad, because it makes it almost impossible for an IDE to understand what is going on.

The question is, is it Groovy’s fault? My answer is not totally. The fault is mostly on the DSL design, and Groovy made it too easy to do so. But again, that was designed at a time when dynamic Groovy was the rule. I gave a talk, recently, about building modern DSLs with Groovy, where I discourage such practices, and encourage the use of static DSLs instead.

That leads me to the second main reason of embracing Kotlin in Gradle: performance. When we talk about performance, lots of folks tend to think that Groovy is slow. This is not the case. Groovy is pretty fast. However, depending on the design of the DSL, you can easily fall into traps that can lead to catastrophic performance. Before I go further with it, I’m reading way to often that Gradle is slow because it’s written in Groovy and that Groovy is dynamic so it’s slow. F* no, those who tell you that just didn’t profile a build. As I said, Gradle is mostly written in Java. And I’ve spent the last 3 months optimizing the performance of Gradle, and I can tell you that of the dramatic performance improvements that one can see in Gradle 2.13 and 2.14, almost none was obtained by rewriting Groovy to Java, or rewriting Groovy code. None! Most of the hotspots were pure Java code. Period. However, as soon as you use plugins, which are today mostly written in dynamic Groovy, or that your build scripts imply a lot of nested closures, things start to become complicated for "Groovy". Let me explain that clearly. I think at some point, someone made a terrible design decision in Groovy. I don’t know who it was, but the idea was to rely on exceptions to control the flow of resolution of properties. This means that when a property is missing, typically in a closure, an exception is thrown. When a method is not found, an exception is thrown. When a property is not found, an exception is thrown. That seemed to be a good idea, because in the end, you want to provide the user with an error, but in practice, this is catastrophic, because Groovy can capture those exceptions. Typically, in a delegation chain (nested closures), a containing closure or class can actually have this property defined, or implement property missing/method missing. Now, re-think a second about the "simple" example of Gradle build above: how do you now where to look up for message, top, signature, …? Now you know: f* exceptions are thrown, stack traces are filled, and eventually captured because some composite dynamic object finally wants to answer the message… In practice, for some builds I have profiled, it was tens of thousands of exceptions being thrown and stack traces filled for nothing. And that has a terrible impact on performance. So even if we have implemented strategies in Gradle to try to avoid throwing those exceptions (which are responsible for part of the performance improvements in 2.14), this is very hard to do it, and we’re still throwing way too many of them. A static language doesn’t have this problem, because every single reference in source is resolved at compile time. So, if you’re writing a plugin in Groovy, for the sake of performance, please add @CompileStatic.

So there goes Kotlin. Kotlin has excellent static builders support, that make it practical both for IDE support, which will dramatically improve user experience in terms of understanding what do write, what is an error, having documentation, refactorings, … and is a very pleasant language to work with. Honestly, I don’t have anything bad to say about the language (apart from the fun keyword that I don’t like). To some degree, it’s not very surprising: Kotlin has heavily inspired by Groovy and another popular JVM language: Scala. And again, being the one behind the static compiler of Groovy, I can’t blame them for doing what I like about static languages. Their builder support is awesome, and very elegant. And it’s supported out of the box by IntelliJ of course, but also Eclipse.

A static DSL for Groovy?

Ok, so one might think at this point that I’m mad. I wrote a "competing" language, and I’m happy to see Kotlin being promoted in Gradle. I wrote the static compiler, that is capable of doing everything Kotlin can do (minus reified generics, plus superior scripting support, type checking extensions, …), so wtf? Ok, so let’s be very clear: I have absolutely no doubt that Groovy can do everything that we’ve done with the Kotlin support in Gradle. It can be statically compiled, provide an elegant DSL that is statically compiled, and it can be understood by the IDE. I had no doubt before the Kotlin work started, I have even less doubts now. And I can say I have no doubts because I tried it: I implemented experimental support for statically compiled Gradle scripts, written in Groovy. Here’s an example:

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'groovy'
apply plugin: GreetingPlugin

repositories {
    mavenCentral()
}

dependencies {
    compile 'commons-lang:commons-lang:2.5'
    compile "commons-httpclient:commons-httpclient:3.0"
    compile "commons-codec:commons-codec:1.2"
    compile "org.slf4j:jcl-over-slf4j:1.7.10"
    compile "org.codehaus.groovy:groovy:2.4.4"
    testCompile 'junit:junit:4.12'
    runtime 'com.googlecode:reflectasm:1.01'
}

tasks.configure('test', Test) {
    jvmArgs '-XX:MaxPermSize=512m', '-XX:+HeapDumpOnOutOfMemoryError'
}

dependencies {
    compile 'org.codehaus:groovy:groovy-all:2.4.4'
}

extension(GreetingPluginExtension) {
    message = 'Hi'
    greeter = findProperty('greeter')?:'static Gradle!'
}

tasks.create('dependencyReport', DependencyReportTask) {
    outputs.upToDateWhen { false }
    outputFile = new File( project.buildDir, "dependencies.txt")
}

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create("greeting", GreetingPluginExtension)
        project.task('hello') << {
            println "${project.extension(GreetingPluginExtension).message} from ${project.extension(GreetingPluginExtension).greeter}"
        }
    }
}

class GreetingPluginExtension {
    String message
    String greeter
}

This is an example Gradle build that is compiled statically. It has none of the problems I described about the Groovy implementation in Gradle above. It uses all the techniques that static Groovy provides: extension methods, powerful scripting with implicit imports, type checking extensions, … All this works. And interestingly, the work that is done to enable support for Kotlin also benefits to statically compiled Groovy, and Java! Let’s not forget about the latter, which is years behind in terms of "modern" languages support. So if this works, why do we need Kotlin? To be honest, I asked it to myself many times. It was very difficult to me, because I knew Groovy could do it. Again, I had no doubt about the language capabilities, no doubt about the performance impact of doing this. However, I missed two critical points:

  1. IDE support. Even if support of Groovy in IntelliJ is by far the most advanced of all other IDEs, it still lacks behind when static compilation is on. But more importantly, it doesn’t know that my script is statically compiled, nor does it now about my custom extension methods. I tried to implement a GDSL descriptor to make it aware of them, and it somehow worked: I do have code completion, but errors are not marked as errors, and the IDE still doesn’t understand that it should only suggest to me what is relevant in the context. With Kotlin scripts which are natively static, there’s no such issue. The IDE understands everything natively, in IntelliJ and Eclipse. So, I have no doubt that Jetbrains can implement support for this, just like I had no doubt I could implement a static Groovy DSL, but who is going to write this? Me? Gradle? I don’t have the time to do it. And it’s not Gradle’s job to write IDE plugins. And what about Eclipse? One big issue that the Groovy community has, today, is that nobody is supporting Eclipse since Pivotal dropped sponsorship of Groovy. After more than one year, nobody took over the development of Groovy Eclipse. Nobody. While Groovy itself saw lots of new contributors, while we saw a lot of bugfixes, new contributors and that the download numbers where never as high as they are today, IDE support is critical. And nobody took over the development of it. I saw some people referring to what Jetbrains is doing as "blackmailing". Seriously? Jetbrains? Think of what they’ve done for Groovy. Groovy would never has been as popular as it is without them. They provided us with the best Groovy IDE possible. They are constantly supporting new features of the language, adding support for AST transformations, traits, … They even added the ability, in IDEA 14, to use Groovy (and not Kotlin, guys!) as the language to evaluate expressions in the debugger. And they would try to kill Groovy? Kill part of their business? Come on guys! So yes, they invested a lot in Kotlin and want to promote it, but how could it be otherwise? And it’s not like if the language sucked: it’s awesome!

  2. Does it make sense? Now that we made the decision to support Kotlin, that we proved it would provide the level of user friendliness we want and that it is statically compiled by default, does it make sense to put resources to support static Groovy in addition? I don’t have an answer to this. I thought yes, but now I’m not sure. Kotlin does the job. And honestly, they have great engineers working on the language. Even if it lacks behind in terms of scripting and compilation times compared to Groovy, I have no doubt they will fix it. How arrogant would we be if we thought other languages could not do what we’ve done with Groovy?

The future of Groovy

The last point I want to address is what it means for the future of Groovy, and what it means for my future in Groovy. First of all, I always thought that the future of Groovy was in the hands of its community. It’s not Gradle that has Groovy’s future in its hands. It’s you. The move to the Apache Software Foundation was also done for this very same reason: community first. If you want to continue to use Groovy, to improve it, to support it, all you have to do is f* do it! And I will continue! I love this language, I know too well how far it can go in terms of DSL support, AST transformations, now in 2.5 we have macros, that’s just a crazily powerful language that’s super fun to use. Should we fear competition? No, we shouldn’t. Competition is good. It should be inspiring. And if Gradle moving to Kotlin means the death of Groovy, maybe the problem is elsewhere. And even if lots of people get introduced to Groovy through Gradle, it’s not the only entry point. Grails is another. Jenkins (through Flow) is another. And many, many more. There was a tweet a few days ago which showed the 100 most popular dependencies in GitHub projects. Groovy was one of them. No Kotlin. No Scala. Groovy. It’s everywhere, and it’s going to be there for a long time.

Part of the fears of the community is, after the Pivotal demise, if Groovy is a dying language. It’s not. It has never been so widely used. The move to Apache Software Foundation drove a lot of attention and brought us many more contributors. But the community has to realize what the problems with Groovy are, and it has to face them: the introduction of the static compiler was too late. IDE support is important. Java 9 support is going to be super important. If you love your language, contribute. Help it. Help yourselves. The future of Groovy must be in your hands. I can’t recall how many times I told this, since I joined VMware, a few years ago, to develop Groovy. In every talk I give, I’m always telling how important it is that you contribute. Jetbrains is not going to write Groovy Eclipse for you.

And I would like to finish with one word: if people move from Groovy to Kotlin, is it really a problem? Isn’t any technology inspired by another? Aren’t we, developers, always rebuilding the same things, but improving them, learning lessons from the past? Is Kotlin a better Groovy? I don’t have the answer yet. Maybe it is. Maybe not. Today Groovy remains greatly superior in terms of scripting, DSL support, but it comes with a price that Gradle doesn’t want to pay. And let’s not forget the original community of Groovy: a dynamic language for the JVM. There are still lots of people who like this aspect of the language (and I do too, typically when I write Groovy scripts in place of bash scripts, I don’t care about types). It’s compile time metaprogramming features also make it incredibly powerful. Modern Groovy definitely doesn’t deserve its "bad press". Would you compare Java 8 with Java 1? No. So don’t compare Groovy 2.4 with Groovy 1 either. Reputation should change, and you can help there too.

This leads me to what I should do. And there, I’m a bit lost, to be honest. I work for a company that embraced Groovy, that is now embracing Kotlin. I love my job, I love working with Gradle, I love Groovy, and I quite enjoy Kotlin. I’m a passionate developer. I just want to continue having fun. But if you think that as such, I’m not a good representative of the Groovy community anymore, maybe I should step off from the Groovy project. I would hate that, but I’ve kind of been hurt by the bad comments we (Gradle) received from some members of the Groovy community. I don’t want to fall into a language war, I don’t care about this. I care about users. What I love to do is helping people, period.

I would like to finish this post with a thought about what I’m going to do, as a Gradle developer, for you, Groovy users. In particular, I am convinced that the success of Gradle is largely due to its Groovy DSL, despite its problems. The fact that it’s simple, easy to read, is super important. I joined the Groovy project because I was using Groovy as a DSL platform in a natural language processing context. Groovy is super powerful for this. And I learnt a lot in terms of DSL design. In particular, I will try to make sure that it doesn’t become a Kotlin API. What I mean by that is that I think we should elevate from a Groovy DSL to a Gradle language. And this language is meant at describing builds. And our users are not Kotlin developers. Most of them are not Groovy developers either. They are, as I described earlier, from different horizons. And I would hate if a user would have to understand concepts like generics or type inference to write a build script. That would be horribly wrong. A build author should understand how to model an application, not what is a type, what is an extension method, or generic return type inference. It’s different for plugin authors, but for a build author, it’s super important. So I will try to make sure that Kotlin scripting support improves, even if it means that it would go even closer to what Groovy supports. I would do this not because I want Groovy to die, I don’t (and it wouldn’t help my royalties for Groovy in Action 2 ;)), but it would help users or Gradle. That’s what I care most about, just like I care about what Groovy users want when I work on the Groovy project.

As for talking about Gradle, Groovy and its future, I’ll be a GR8Conf next week, I’d be happy to answer you in person there too!

Keep on Groovying!

Comments

A week fighting a PermGen leak

29 août 2015

Tags: jvm groovy gradle permgen yourkit

A challenge at Gradle

A new job

This is my first blog post after 4 months being a full time employee of Gradle Inc.! I am pretty excited by this job, even though so far it didn’t give me much time to contribute to Apache Groovy (but we did manage to release 2.4.4 though). One of the reasons I joined the company was because I love technical challenges. And Gradle has a lot of them, with an incredible team of smart people working together to make software automation better. This week, I worked on my first true challenge, and I must confess that I miserably failed :-) This is a long post, and anyone who ever fought against the infamous "PermGen space error" in their application is going to understand why…

CodeNarc as the source of a leak?

The first suspicious piece of code that draw our attention was CodeNarc. CodeNarc is a source code quality analysis tool for Groovy, which is used by a lot of Groovy developers, including in Gradle itself (since Gradle intensively makes use of Groovy). CodeNarc can be seen as the equivalent of FindBugs for Groovy code. And problems seemed to start with an upgrade of the Gradle CodeNarc plugin to use CodeNarc 0.23. We actually saw reports like this one in the forums or this other one in GitHub but thought that the PermGen error was just a consequence of CodeNarc including more rules: rules are written in Groovy, so compiled down to classes and classes eat PermGen. So increasing the PermGen space was enough, and it did actually solve the error. Problem solved. Or not. The riddle only started for me with a seemingly insignificant question on our internal mailing lists: "Can some investigate why our build sometimes fails with a PermGen space error?", and I volunteered.

Interestingly, I had just finished pushing an upgrade of Gradle to Groovy 2.4.4 on our master branch, and I had noticed that I had to increase the PermGen space too. At first, I naively thought that it was also required because Groovy 2.4 consumed more memory, but I was wrong. I should have known, because before joining Gradle, I had actually worked on Groovy for Android, and a consequence of this work was that Groovy 2.4 had a reduced memory footprint: we generate less bytecode, which directly relates to a reduced PermGen space usage. So why on earth would Groovy 2.4 require more memory? And what is the relation with the CodeNarc plugin? Actually this plugin works in a Gradle version that uses Groovy 2.3.10, so why would there be a relation between the two?

In such a case, your best friend is a profiler. But as I will explain here, it can also lead you to wrong tracks. Be careful. The second best friend is the JVM options -XX:+TraceClassLoading and +XX:+TraceClassUnloading. I also used -XX:SoftRefLRUPolicyMSPerMB=0, an option that I had no idea it existed before my friend David Gageot told me. Basically, it will force the garbage collector to agressively collect all soft references, which is very useful to understand, in combination with the 2 other options, from which classloader we are leaking memory.

The first wrong track was actually thinking that CodeNarc was the source of the problem. I wrote an email on the gradle-dev list, explaining my findings, and I had indeed found a lot of classes from CodeNarc were not unloaded. Before I go further, let’s explain how the JVM is supposed to behave with regards to classes in Java 7. We all know that objects are garbage collected, but for a lot of people, classes are not. That’s why we have the PermGen space (which has disappeared in Java 8 but that’s another story): this segregated space of the JVM memory is used to store classes. And in Java, a class is loaded by a classloader. There is a strong reference between a class and its classloader. But what the JVM is able to do is actually simple: if there’s no instance of the class which is strongly reachable and that the classloader is neither strongly reachable, then both the class and the classloader can be unloaded. This means that PermGen can be recovered, and it is pretty useful, especially for a language like Groovy which can generate a lot of classes at runtime.

In Gradle, and particularily in the Gradle CodeNarc plugin, CodeNarc is executed through an Ant task, which spawns its own isolated classloader, containing both the CodeNarc and Groovy classes. So when the plugin execution is finished, if we do not keep track of the classloader, classes should be garbage collected. So a good candidate for the memory leak was actually the IsolatedAntBuilder that Gradle uses to execute the Ant task. And guess what? There is such a leak, because the DefaultIsolatedAntBuilder performs classloader caching! That was also discovered by my colleague Sterling, who immediately spot that: while we do cache the classloaders, keeping a strong reference on them, we don’t have any code to release the classloader in case of memory pressure. Conclusion, we’ve found the memory leak, hurray! And it has nothing to do with CodeNarc or Apache Groovy, pfiew!

So I immediately tried to disable the cache, which turned to be pretty trivial. Run the build again and… another PermGen space error. No CodeNarc classes unloaded, no Groovy classes unloaded. Wow. So the problem wasn’t solved, first "oh my!" moment of the week: there was another leak.

One test I did, then, is to totally comment out the code that, in the ant builder code, performed the definition of the CodeNarc task. Eventually, there was only that code left in the CodeNarc plugin:

    @TaskAction
    void run() {
        logging.captureStandardOutput(LogLevel.INFO)
        def classpath = new DefaultClassPath(getCodenarcClasspath())
        antBuilder.withClasspath(classpath.asFiles).execute {
            // ... thou shalt not leak!
        }
    }

I executed the code again, and there was definitely a leak: after several loops, a PermGen error occurred. CodeNarc was ruled out as the source of the leak. After some hours of trials, study of memory snapshots, I eventually came out with a piece of code that reproduces the problem independently of Gradle:

int i = 0;
try {
    while (true) {
        i++;
	URLClassLoader loader = new URLClassLoader(
            new URL[]{new File(GROOVY_JAR).toURI().toURL()},
            ClassLoader.getSystemClassLoader().getParent());
        Class system = loader.loadClass("groovy.lang.GroovySystem");
        system.getDeclaredMethod("getMetaClassRegistry").invoke(null);
        loader.close();
    }
} catch (OutOfMemoryError e) {
    System.err.println("Failed after " + i + " loadings");
}

As you can see, the code is very simple: it creates a new isolated classloader, which only contains Groovy on classpath. Then it invokes the creation of the Groovy runtime, by asking the metaclass registry, then it closes the classloader. On my JVM, after about 40 runs, the code fails with a PermGen space error: despite the fact that no class, no object is kept out of the classloader, the Groovy runtime is not unloaded, leading to a memory leak. The key point here is that I had noticed some oddities during my hours of debugging: a class, named ClassInfo, was at the center of those oddities.

ClassInfo leak

In particular, although YourKit (the profiler I was using) was telling me that all classes, all classloaders were weakly or softly reachable (btw, it’s really a pity that YourKit doesn’t show them separately, that is, weakly referenced objects from softly referenced ones), the classes were not garbage collected. Also strangely, some classes appeared as GC roots, meaning they could be collected, but they weren’t! And when I navigated through some of the duplicate classes I was seeing, ClassInfo was present, as a value of a map entry of class value. Here we are, I had found the real source of the leak. Something had changed in Groovy. And the fact that CodeNarc was leaking since 0.23 was just a side effect of upgrading its dependency to Groovy 2.4! So despite Gradle was using Groovy 2.3.10, CodeNarc, by default, was using a more recent version of Groovy. That doesn’t explain why it leaks yet, but we now know who is responsible: Groovy.

ClassValue, friend or foe?

Since Groovy 2.4, Groovy uses a new mechanism for storing its runtime metadata in case you run on JDK 7 or later: ClassValue. ClassValue allows storing information on the class level. Typically, a language like Groovy would use it to store the metaclass of a class. In practice, Groovy doesn’t directly store the metaclass here, but a higher level concept called ClassInfo, which in turns gives access to the metaclass of a class.

Before Groovy 2.4, all that information was stored directly in the ClassInfo class itself, through a private static field. ClassInfo is therefore the global entry point for accessing runtime information about a class. While Groovy 2.4 still uses ClassInfo as an entry point, there are 2 possible storage mechanisms, based on the underlying JDK. If ClassValue is available; which is the case for any JDK 7+, then it is used, otherwise we fallback on the old mechanism. ClassValue is supposed to be more efficient and more direct. Of course, in any case (old and new storage mechanism), it is memory sensitive: in case a class is not available anymore, its ClassInfo is removed. What ClassValue storage provides is basically the same as ThreadLocal, but at the Class level instead of the Thread level. Users are allowed to store information here, but one should be aware that as thread locals, if you start using it, you may face memory leaks if you don’t use it properly.

That’s for the theory, let’s see how in practice this change led to a giant memory leak in the Gradle build.

The theory is that ClassValue should behave like ThreadLocal. That is, the entries stored in the internal map of the Class class, should be garbage collected when the referent is not strongly referenced anymore. This behavior is however not what the JVM does. It was confirmed to me by Charles Nutter (JRuby) a few hours later: although we all expected the JVM to collect the unreachable, it does not.

Uh. Second "oh my!" moment of the week. I had now a candidate (Groovy) and a reason (ClassInfo leaking). However, it doesn’t explain by itself why the class loader is not garbage collected: if Groovy stores information on classes, it’s ok, as long as the classes to which it writes some metadata are from the classloader, or any child classloader, of the Groovy runtime itself. Everything would be self-contained, meaning we would have a graph of objects that do not leak outside of the isolated classloader. However… Groovy uses Strings, integers, List, … all coming from the system classloader. And that is the main difference with the old metadata storage mechanism: the old one only referenced classes from the system classloader. With ClassValue, we are modifying classes from the system classloader too! That is, the String class, for example, contains in its class value map, information from the Groovy runtime! The famous ClassInfo instance is present there! There we are! We leaked a ClassInfo instance into the system classloader! So what happens is that when we are done with our "isolated" Groovy runtime, we think it should unload because nothing references any object or class from that classloader. However, the Groovy runtime did update classes from the system classloader, and it started leaking into it! The ghost in the shell! Groovy is spoiling everywhere…

So far so good, I had the explanation, I could write a workaround: let’s iterate over all those classes that Groovy updated, remove the ClassInfo, and we’re done. I wrote that code, and it turned out to be a bit ugly, but… it worked! Here is, for information, the cleanup code:

    static void removeClassFromGlobalClassSet(Class<?> classInfoClass) throws Exception {
        Field globalClassValueField = classInfoClass.getDeclaredField("globalClassValue");
        globalClassValueField.setAccessible(true);
        Object globalClassValue = globalClassValueField.get(null);
        Method removeFromGlobalClassValue = globalClassValueField.getType().getDeclaredMethod("remove", Class.class);
        removeFromGlobalClassValue.setAccessible(true);

        Field globalClassSetField = classInfoClass.getDeclaredField("globalClassSet");
        globalClassSetField.setAccessible(true);
        Object globalClassSet = globalClassSetField.get(null);
        globalClassSetField = globalClassSet.getClass().getDeclaredField("items");
        globalClassSetField.setAccessible(true);
        Object globalClassSetItems = globalClassSetField.get(globalClassSet);

        Field clazzField = classInfoClass.getDeclaredField("klazz");
        clazzField.setAccessible(true);


        Iterator it = (Iterator) globalClassSetItems.getClass().getDeclaredMethod("iterator").invoke(globalClassSetItems);

        while (it.hasNext()) {
            Object classInfo = it.next();
            Object clazz = clazzField.get(`ClassInfo`);
            removeFromGlobalClassValue.invoke(globalClassValue, clazz);
        }

    }

After executing that code, no ClassInfo instance was leaking anymore into the system classloader, and the runtime could be shutdown properly. The garbage collector did its job, and yay! I’m so happy, I’ll be able to sleep soon! That was tuesday night. And that night, I thought I had found the solution.

Memory sensitive classloader caching

So wednesday, I spent the day trying to implement the same strategy inside Gradle. More precisely, inside the IsolatedAntBuilder thing I told you. I implemented the code, launched my test again and hurray! it worked! My test passed! No more PermGen space error! So all I had to do, now, was to reactivate classloader caching, otherwise, we would loose a feature that is important performance wise.

So I reactivated the cache, and boom! That time, the Gradle build did not fail with a PermGen error, but with very strange errors like this one:

groovy.lang.MissingMethodException: No signature of method: java.lang.Integer.plus() is applicable for argument types: (java.lang.Integer) values: [0]
>  Possible solutions: plus(java.lang.String), plus(java.lang.Character), abs(), use([Ljava.lang.Object;), split(groovy.lang.Closure), minus(java.lang.Character)

Mmmmmm… 3rd "oh my!" moment of the week. I understood what I had just done. By clearing the ClassInfo stuff from the classloader, I had effectively shutdown the Groovy runtime that was initiated in that cached classloader. So when some code was trying to reuse the runtime from that cached classloader, since I had disabled it, it was failing! And there’s no option to reinitialize the Groovy runtime. It’s just not doable, because everything happens in static initializers (private final fields, …). So unless the JVM had an option to allow to re-execute the static initializers of a class (and who knows what oddities it would lead to), I had no luck.

That’s about when I told my mates at Gradle "I think we have to choose between caching and leaking memory". But the night came, and I actually had an idea. I could implement a memory sensitive cache: by writing a smart cache structure with appropriate SoftReferences and reference queues, I would be able to execute the shutdown code only when I know that the GC is trying to reclaim memory. The idea is simple: we have a map, which key is a SoftRefence<String>, and the value is our cached classloader. The String represents the classpath that we are caching for the classloader.

Now imagine that the GC is out of memory. The semantics of SoftReference are clear: before throwing an OutOfMemoryError, the JVM will do its best and clear all soft references. Doing so, using a custom reference queue, we can be notified that the reference is cleared. Then, we can execute the Groovy runtime shutdown code, which will in turn make the ClassLoader collectible.

Honestly I was pretty happy with my implementation. I executed the code and it worked! Caching was working until the GC tried to reclaim memory, then I saw my shutdow code executed, memory reclaimed and green tests. Woooo!!! I had eventually knocked that memory leak down! Ha ha!

Then I remembered that my colleague Sterling had a test which involved a loop in an integration test. To make sure I had really fixed the leak, I asked him to tell me how he did that. The code was very simple, just involving a loop thanks to @Unroll in a Spock specification. I did it and… PermGen error showed up again. WAT?!

That was the fourth "oh my!" moment. And not the last one. I really then spent hours in modifying my caching code, refactored my code to add more complicated memory leak strategies, seeing that there were still thread locals, clearing them explicitly, adding a memory leak strategy for Ant itself, for the Java Beans introspector, … None of my attempts worked. In the end, it always failed. But there was always one mystery: I saw that the Groovy classes were unloading. But the Ant classes were not… And the rest, I should have discovered that much sooner. But when you have so many potential source leaks, that are much more evident, it’s so hard to figure out.

In particular, one thing would have made things much easier to discover. In YourKit, you can see that there are duplicate classes. Classes that have the same name, but come from different class loaders. However, there’s nothing that will show you those duplicates. You have to find them yourself. And in the end, when in the dump you see an instance of that class, all you can see is that it is an instance of ClassInfo. Nothing, visually, tells you that the instance of ClassInfo that you are seeing actually comes from a different classloader from the one you are seeing just next to it. A bit of color, for example, would help…. And it would have helped me seeing that some ClassInfo elements that I was seeing in the classes from Ant didn’t come from the "disposable" Groovy runtime… No. They were coming from… the Gradle runtime itself!

Where it all ends

Ok, That was the last "oh my!" moment of the week. The one that killed all my hopes. And to understand the problem, I now have to explain to you how IsolatedAntBuilder works. It’s a very small, yet very smart and practical piece of code. Maybe too smart.

Gradle, as a core feature, lets you execute Ant tasks thanks to code inherited from the Groovy codebase itself: AntBuilder. It’s a very elegant piece of code, that lets you write things like:

task check << {
    ant.taskdef(name: 'pmd',
                classname: 'net.sourceforge.pmd.ant.PMDTask',
                classpath: configurations.pmd.asPath)
    ant.pmd(shortFilenames: 'true',
            failonruleviolation: 'true',
            rulesetfiles: file('pmd-rules.xml').toURI().toString()) {
        formatter(type: 'text', toConsole: 'true')
        fileset(dir: 'src')
    }
}

While this works, there’s actually a lot involved behind that. Including classloader magic. In particular, in the example above, we create a task definition in Ant, which uses a classpath defined in Gradle. "ant" here is a global object which is shared accross the build, but it is possible to avoid the classes from the Ant tasks to be mixed with the Gradle classpath itself by using antBuilder instead. That’s what the CodeNarc plugin does:

antBuilder.withClasspath(classpath.asFiles).execute {
}

means "Gradle, please, create an isolated classloader for me, that will contain the classpath only necessary for CodeNarc, and execute that Ant task with it". It seems very trivial, but there is a problem. The code that you see here is found in a Gradle script. It means that the "antBuilder" object that you are seeing here comes from Gradle. It is our IsolatedAntBuilder instance. When we call "withClasspath", a new instance of IsolatedAntBuilder will be created, with an isolated classloader corresponding to the supplied classpath. Then calling execute with a closure that lets you configure the ant task using the Groovy AntBuilder syntax.

So the "Closure" class that we are seeing here comes from Gradle. Then, we have a classloader which contains the Ant runtime, and a "bridge" class, written in Groovy, called "AntBuilderDelegate", which has one responsibility: when the code of the Ant builder is going to be executed, it is likely that the version of Groovy which will be found on classpath will be different from the one that Gradle uses. That is exactly what happens with CodeNarc: Gradle 2.6 uses Groovy 2.3.10, but the CodeNarc plugin executes with Groovy 2.4.1, so the Ant task works with a different "Closure" class than the one that Gradle has. We will really have two distinct "Closure" classes here, and "AntBuilderDelegate" is responsible for filling the gap: when the Ant configuration code, which will use AntBuilder from the Ant classpath, is going to be executed, it will be calling AntBuilderDelegate instead of directly the Closure code. And that code will intercept the missing methods in order to "reroute" them to the builder. You don’t have to understand that in detail, it’s not really the point here, but it is important to understand that this "AntBuilderDelegate" class is instantiated… in Gradle, using the Gradle classloader.

Now you may see it coming. I told you I had upgraded Gradle to use Groovy 2.4.4. So what does it mean? Gradle now uses Groovy with ClassValue. And what is the problem with ClassValue? All classes "touched" by Groovy will have them "polluted" with necessary metadata information. So when we create an instance of "AntBuilderDelegate", we’re doing that using the Groovy 2.4 runtime from Gradle, which comes with its own ClassInfo. And that delegate references and AntBuilder which is instantiated using the Ant classloader, with a different Groovy runtime, having its own ClassInfo. So what I had found earlier was that the ClassInfo from the Groovy "Ant" runtime was leaking into Gradle. But I hadn’t realized that the opposite was also true! By bridging the runtimes, we were leaking Groovy "Gradle" into the isolated classloader, through ClassValue!

So, what happened, is that the Groovy classes from the isolated classpath were garbage collected, because no ClassInfo from Gradle leaked into them. However, the Ant classes were touched. And they were NOT collectible then.

And this is were I stopped. Because if I found a way to "unload" ClassInfo from the isolated classpath and the touched classes from the system classloader, I haven’t found a way to do the same for the ClassInfo instances that leak into the Ant runtime… Of course I tried a "brute force" thing, by removing all ClassInfo from those classes, but as you understood, it’s a desperate attempt: it’s equivalent to shutting down the runtime. And then, it totally breaks subsequent calls in the Gradle build, we’ve just broken the Groovy runtime from Gradle…

To add some confusion to the problem, I think it’s now a good time to explain that I actually simplified the isolated ant builder classloader hierarchy. There are actually (at least) 3 classloaders involved:

  • The classloader from Gradle, which loads the Gradle runtime, the IsolatedAntBuilder instance and also the AntBuilderDelegate instance

  • A classloader for the Ant runtime, which is isolated from Gradle, apart from logging classes necessary for Gradle to be able to capture the output. This classloader is per classpath, and is the one which is cached.

  • A classloader that is filtering some classes from the Gradle runtime classloader to make them available. This is what the bridging Ant builder uses. This classloader is shared among all isolated ant builder instances.

So when I say that something leaks, it can leak to any of those classloaders, and any of the parent loaders…

That’s were I felt desperate. After a week fighting those memory leaks, and so many "ah ah, got you!" moments, I was in front of a wall. Basically, while in the first case (isolated ClassInfo leaking into Gradle), I know I can totally clean all the ClassInfo references because I know I can shutdown the Groovy runtime, in the second case (Gradle ClassInfo leaking), I basically have no idea that a class comes from the isolated classloader or not. So it’s hard to say if you should remove the ClassInfo or not. I am currently experimenting a brute force "try to determine if a class belongs in the class loader hierarchy", but it is weak (ah ah!) because I need to know about several potential class loader types.

What’s next?

So, what can we do next? One has to remember that fixing Groovy is not the ultimate solution because Gradle uses Groovy internally, but the various tasks can very well use a different Groovy version which is beyond our control.

  1. rollback Groovy in Gradle to use Groovy 2.3.10. It would avoid the Groovy classes from Gradle to leak into the Isolated classloaders, but is also unfortunate given the improvements that this version provides. Also, those who write Groovy applications for Android use Groovy 2.4+…

  2. CodeNarc would still use Groovy 2.4+, we could downgrade it too. However, if people rely on features of Groovy 2.4+, they just have no choice, so we would still have the problem.

  3. Use smarter techniques like instrumentation to track the leakages of ClassInfo, record them, and revert when we’re done. It’s doable, but it’s a huge amount of work, and relying on instrumentation for Gradle would be very bad for performance.

  4. Update Gradle to use FilteringClassLoader everywhere, including in its main process, to prevent ClassValue to be found. This would work because without that class, Groovy wouldn’t use ClassValue to store the metadata and fall back to the old mechanism.

  5. Wait for a fix in Groovy. Jochen is already working on that, but we know that the old mechanism isn’t perfect either, and has memory leaks too. That was one of the reasons to migrate to ClassValue.

  6. Wait for a fix of the JVM. That’s beyond our control.

  7. Increase the PermGen space for builds that use the code quality plugins, which internally use AntBuilder. It’s what we do today. It works, but it’s just hiding the problem. And we have to explain to our users to do it too.

  8. Some smart people come with a smart solution, that’s why I wrote this too. During that week, I got help from lots of people, including Jochen "Groovy" Theodorou, Henri "EasyMock" Tremblay, David Gageot or Nikita Salnikov from Plumbr, thank you guys!

By the way, if you wonder, the same problem exist in JDK 8 too, it’s just not visible immediately because of the metaspace that appeared to replace the PermGen space.

Now, it was fun writing this "post-mortem", I hope it wasn’t too obscure, it helped me a lot because I had so many "got it" and "oh noes!" moments that I felt it was very interesting to share this story with you. And if you like technical challenges, do not forget that Gradle is hiring!

Update

After writing this post, I made great progress, and I did manage to get rid of the leak, but I also discovered that the leak happens with the various GroovyCompile tasks… Anyway you can follow my progress on this branch.

Comments

Improved sandboxing of Groovy scripts

27 mars 2015

Tags: groovy sandoxing type checking AST secure

One of the most current uses cases of Groovy is scripting. Groovy makes it very easy to execute code dynamically, at runtime. Depending on the application, scripts can be found in multiple sources: file system, databases, remote services, … but more importantly, the designer of the application which executes scripts is not necessarily the one writing those scripts. Moreover, the scripts might run in a constrained environment (limited memory, file descriptors, time, …) or your you simply don’t want to allow users to access the full capabilities of the language from a script.

What you will learn in this post
  • why Groovy is a good fit to write internal DSLs

  • what it implies in terms of security in your applications

  • how you can customize compilation to improve the DSL

  • the meaning of SecureASTCustomizer

  • what are type checking extensions

  • how you can rely on type checking extensions to offer proper sandboxing

For example, imagine that you want to offer users the ability to evaluate mathematical expressions. One option would be to implement your own internal DSL, create a parser and eventually an interpreter for those expressions. Obviously this involves a bit of work, but if in the end you need to improve performance, for example by generating bytecode for the expressions instead of evaluating them, or introduce caching of those runtime generated classes, then Groovy is probably a very good option.

There are lots of options available, described in the documentation but the most simple example is just using the Eval class:

Example.java
int sum = (Integer) Eval.me("1+1");

The code 1+1 is parsed, compiled to bytecode, loaded and eventually executed by Groovy at runtime. Of course in this example the code is very simple, and you will want to add parameters, but the idea is that the code which is executed here is arbitrary. This is probably not what you want. For a calculator, you want to allow expressions like:

1+1
x+y
1+(2*x)**y
cos(alpha)*r
v=1+x

but certainly not

println 'Hello'
(0..100).each { println 'Blah' }
Pong p = new Pong()
println(new File('/etc/passwd').text)
System.exit(-1)
Eval.me('System.exit(-1)') // a script within a script!

This is where things start to become complicated, and where we start seeing actually multiple needs:

  • restricting the grammar of the language to a subset of its capabilities

  • preventing users from executing unexpected code

  • preventing users from executing malicious code

The calculator example is pretty simple, but for more complex DSLs, people might actually start writing problematic code without noticing, especially if the DSL is suffiently elegant to be used by non developers.

I was in this situation a few years ago, when I designed an engine that used Groovy "scripts" written by linguists. One of the problems was that they could unintentionally create infinite loops, for example. Code was executing on the server, and then you had a thread eating 100% of the CPU and had no choice but restart the application server so I had to find a way to mitigate that problem without compromising the DSL nor the tooling or the performance of the application.

Actually, lots of people have similar needs. During the past 4 years, I spoke to tons of users who had a similar question: How can I prevent users from doing bad things in Groovy scripts?

Compilation customizers

At that time, I had implemented my own solution, but I knew that other people also had implemented similar ones. In the end, Guillaume Laforge suggested to me that I wrote something that would help fixing those issues and make into Groovy core. This happened in Groovy 1.8.0 with compilation customizers.

Compilation customizers are a set of classes that are aimed at tweaking the compilation of Groovy scripts. You can write your own customizer but Groovy ships with:

  • an import customizer, which aims at adding imports transparently to scripts, so that users do not have to add "import" statements

  • an AST (Abstract Syntax Tree) transformation customizer, which allows to add AST transformations transparently to scripts

  • a secure AST customizer, which aims at restricting the grammar and syntactical constructs of the language

The AST transformation customizer allowed me to solve the infinite loop issue, by applying the @ThreadInterrupt transformation, but the SecureASTCustomizer is probably the one which has been the most misinterpreted of the whole.

I must apologize for this. Back then, I had no better name in mind. The important part of SecureASTCustomizer is AST. It was aimed at restricting access to some features of the AST. The "secure" part is actually not a good name at all, and I will illustrate why. You can even find a blog post from Kohsuke Kawagushi, of Jenkins fame, named Groovy SecureASTCustomizer is harmful[http://kohsuke.org/2012/04/27/groovy-secureastcustomizer-is-harmful/]. It is very true. The SecureASTCustomizer has never been designed with sandboxing in mind. It was designed to restrict the language at compile time, not runtime. So a much better name, in retrospect, would have been GrammarCustomizer. But as you’re certainly aware, there are two hard things in computer science: cache invalidation, naming things and off by one errors.

So imagine that you think of the secure AST customizer as a way of securing your script, and that you want to use this to prevent a user from calling System.exit from a script. The documentation says that you can prevent calls on some specific receivers by defining either a blacklist or a whitelist. In terms of securing something, I would always recommand to use a whitelist, that is to say to list explicitly what is allowed, rather than a blacklist, saying what is disallowed. The reason is that hackers always think of things you don’t, so let’s illustrate this.

Here is how a naive "sandbox" script engine could be configured using the SecureASTCustomizer. I am writing the examples of configuration in Java, even though I could write them in Groovy, just to make the difference between the integration code and the scripts clear.

public class Sandbox {
    public static void main(String[] args)  {
        CompilerConfiguration conf = new CompilerConfiguration();				(1)
        SecureASTCustomizer customizer = new SecureASTCustomizer();				(2)
        customizer.setReceiversBlackList(Arrays.asList(System.class.getName()));		(3)
        conf.addCompilationCustomizers(customizer);						(4)
        GroovyShell shell = new GroovyShell(conf);						(5)
        Object v = shell.evaluate("System.exit(-1)");						(6)
        System.out.println("Result = " +v);							(7)
    }
}
1 create a compiler configuration
2 create a secure AST customizer
3 declare that the System class is blacklisted as the receiver of method calls
4 add the customizer to the compiler configuration
5 associate the configuration to the script shell, that is, try to create a sandbox
6 execute a nasty script
7 print the result of the execution of the script

If you run this class, when the script is executed, it will throw an error:

General error during canonicalization: Method calls not allowed on [java.lang.System]
java.lang.SecurityException: Method calls not allowed on [java.lang.System]

This is the result of the application of the secure AST customizer, which prevents the execution of methods on the System class. Success! Now we have secured our script! Oh wait…

SecureASTCustomizer pwned!

Secure you say? So what if I do:

def c = System
c.exit(-1)

Execute again and you will see that the program exits without error and without printing the result. The return code of the process is -1, which indicates that the user script has been executed! What happened? Basically, at compile time, the secure AST customizer is not able to recognize that c.exit is a call on System, because it works at the AST level! It analyzes a method call, and in this case the method call is c.exit(-1), then gets the receiver and checks if the receiver is in the whitelist (or blacklist). In this case, the receiver is c and this variable is declared with def, which is equivalent to declaring it as Object, so it will think that c is Object, not System!

Actually there are many ways to workaround the various configurations that you can make on the secure AST customizer. Just for fun, a few of them:

((Object)System).exit(-1)
Class.forName('java.lang.System').exit(-1)
('java.lang.System' as Class).exit(-1)

import static java.lang.System.exit
exit(-1)

and there are much more options. The dynamic nature of Groovy just makes it impossible to resolve those cases at compile time. There are solutions though. One option is to rely on the JVM standard security manager. However this is a system wide solution which is often considered as a hammer. But it also doesn’t really work for all cases, for example you might not want to prevent creation of files, but only reads for example…

This limitation - or should I say frustration for lots of us - led several people to create a solution based on runtime checks. Runtime checks do not suffer the same problem, because you will have for example the actual receiver type of a message before checking if a method call is allowed or not. In particular, those implementations are of particular interest:

However none of those implementations is totally secure or reliable. For example, the version by Kohsuke relies on hacking the internal implementation of call site caching. The problem is that it is not compatible with the invokedynamic version of Groovy, and those internal classes are going to be removed in future versions of Groovy. The version by Simon, on the other hand, relies on AST transformations but misses a lot of possible hacks.

As a result, with friends of mine Corinne Krych, Fabrice Matrat and Sébastien Blanc, we decided to create a new runtime sandboxing mechanism that would not have the issues of those projects. We started implementing this during a hackathon in Nice, and we gave a talk about this last year at the Greach conference. It relies on AST transformations and heavily rewrites the code in order to perform a check before each method call, property access, increment of variable, binary expression, … The implementation is still incomplete, and not much work has been done because I realized there was still a problem in case of methods or properties called on "implicit this", like in builders for example:

xml {
   cars {				 // cars is a method call on an implicit this: "this".cars(...)
     car(make:'Renault', model: 'Clio')
   }
}

As of today I still didn’t find a way to properly handle this because of the design of the meta-object protocol in Groovy, that here relies on the fact that a receiver throws an exception when the method is not found before trying another receiver. In short, it means that you cannot know the type of the receiver before the method is actually called. And if it is called, it’s already too late…

Until earlier this year I had still no perfect solution to this problem, in case the script being executed is using the dynamic features of the language. But now has come the time to explain how you can significantly improve the situation if you are ready to loose some of the dynamism of the language.

Type checking

Let’s come back to the root problem of the SecureASTCustomizer: it works on the abstract syntax tree and has no knowledge of the concrete types of the receivers of messages. But since Groovy 2, Groovy has optional compilation, and in Groovy 2.1, we added type checking extensions.

Type checking extensions are very powerful: they allow the designer of a Groovy DSL to help the compiler infer types, but it also lets you throw compilation errors when normally it should not. Type checking extensions are even used internally in Groovy to support the static compiler, for example to implement traits or the markup template engine.

What if, instead of relying on the information available after parsing, we could rely on information from the type checker? Take the following code that our hacker tried to write:

((Object)System).exit(-1)

If you activate type checking, this code would not compile:

1 compilation error:

[Static type checking] - Cannot find matching method java.lang.Object#exit(java.lang.Integer). Please check if the declared type is right and if the method exists.

So this code would not compile anymore. But what if the code is:

def c = System
c.exit(-1)

You can verify that this passes type checking by wrapping the code into a method and running the script with the groovy command line tool:

@groovy.transform.TypeChecked // or even @CompileStatic
void foo() {
  def c = System
  c.exit(-1)
}
foo()

Then the type checker will recognize that the exit method is called on the System class and is valid. It will not help us there. But what we know, if this code passes type checking, is that the compiler recognized the call on the System receiver. The idea, then, is to rely on a type checking extension to disallow the call.

A simple type checking extension

Before we dig into the details about sandboxing, let’s try to "secure" our script using a traditional type checking extension. Registering a type checking extension is easy: just set the extensions parameter of the @TypeChecked annotation (or @CompileStatic if you want to use static compilation):

@TypeChecked(extensions=['SecureExtension1.groovy'])
void foo() {
  def c = System
  c.exit(-1)
}
foo()

The extension will be searched on classpath in source form (there’s an option to have precompiled type checking extensions but this is beyond the scope of this blog post):

SecureExtension1.groovy
onMethodSelection { expr, methodNode ->					(1)
   if (methodNode.declaringClass.name=='java.lang.System') {		(2)
      addStaticTypeError("Method call is not allowed!", expr)		(3)
   }
}
1 when the type checker selects the target method of a call
2 then if the selected method belongs to the System class
3 make the type checker throw an error

That’s really all needed. Now execute the code again, and you will see that there’s a compile time error!

/home/cchampeau/tmp/securetest.groovy: 6: [Static type checking] - Method call is not allowed!
 @ line 6, column 3.
     c.exit(-1)
     ^

1 error

So this time, thanks to the type checker, c is really recognized as an instance of class System and we can really disallow the call. This is a very simple example, but it doesn’t really go as far as what we can do with the secure AST customizer in terms of configuration. The extension that we wrote has hardcoded checks, but it would probably be nicer if we could configure it. So let’s start working with a bit more complex example.

Imagine that your application computes a score for a document and that you allow the users to customize the score. Then your DSL:

  • will expose (at least) a variable named score

  • will allow the user to perform mathematical operations (including calling methods like cos, abs, …)

  • should disallow all other method calls

An example of user script would be:

abs(cos(1+score))

Such a DSL is easy to setup. It’s a variant of the one we defined earlier:

Sandbox.java
CompilerConfiguration conf = new CompilerConfiguration();
ImportCustomizer customizer = new ImportCustomizer();
customizer.addStaticStars("java.lang.Math");                        (1)
conf.addCompilationCustomizers(customizer);
Binding binding = new Binding();
binding.setVariable("score", 2.0d);                                 (2)
GroovyShell shell = new GroovyShell(binding,conf);
Double userScore = (Double) shell.evaluate("abs(cos(1+score))");    (3)
System.out.println("userScore = " + userScore);
1 add an import customizer that will add import static java.lang.Math.* to all scripts
2 make the score variable available to the script
3 execute the script
There are options to cache the scripts, instead of parsing and compiling them each time. Please check the documentation for more details.

So far, our script works, but nothing prevents a hacker from executing malicious code. Since we want to use type checking, I would recommand to use the @CompileStatic transformation transparently:

  • it will activate type checking on the script, and we will be able to perform additional checks thanks to the type checking extension

  • it will improve the performance of the script

Adding @CompileStatic transparently is easy. We just have to update the compiler configuration:

ASTTransformationCustomizer astcz = new ASTTransformationCustomizer(CompileStatic.class);
conf.addCompilationCustomizers(astcz);

Now if you try to execute the script again, you will face a compile time error:

Script1.groovy: 1: [Static type checking] - The variable [score] is undeclared.
 @ line 1, column 11.
   abs(cos(1+score))
             ^

Script1.groovy: 1: [Static type checking] - Cannot find matching method int#plus(java.lang.Object). Please check if the declared type is right and if the method exists.
 @ line 1, column 9.
   abs(cos(1+score))
           ^

2 errors

What happened? If you read the script from a "compiler" point of view, it doesn’t know anything about the "score" variable. You, as a developer, know that it’s a variable of type double, but the compiler cannot infer it. This is precisely what type checking extensions are designed for: you can provide additional information to the compiler, so that compilation passes. In this case, we will want to indicate that the score variable is of type double.

So we will slightly change the way we transparently add the @CompileStatic annotation:

ASTTransformationCustomizer astcz = new ASTTransformationCustomizer(
        singletonMap("extensions", singletonList("SecureExtension2.groovy")),
        CompileStatic.class);

This will "emulate" code annotated with @CompileStatic(extensions=['SecureExtension2.groovy']). Of course now we need to write the extension which will recognize the score variable:

SecureExtension2.groovy
unresolvedVariable { var ->			(1)
   if (var.name=='score') {			(2)
      return makeDynamic(var, double_TYPE)	(3)
   }
}
1 in case the type checker cannot resolve a variable
2 if the variable name is score
3 then instruct the compiler to resolve the variable dynamically, and that the type of the variable is double

You can find a complete description of the type checking extension DSL in http://docs.groovy-lang.org/latest/html/documentation/#type_checking_extensions[this section of the documentation], but you have here an example of _mixed mode compilation : the compiler is not able to resolve the score variable. You, as the designer of the DSL, know that the variable is in fact found in the binding, and is of the double, so the makeDynamic call is here to tell the compiler: "ok, don’t worry, I know what I am doing, this variable can be resolved dynamically and it will be of type double". That’s it!

First completed "secure" extension

Now it’s time to put this altogether. We wrote a type checking extension which is capable of preventing calls on System on one side, and we wrote another which is able to resolve the score variable on another. So if we combine both, we have a first, complete, securing type checking extension:

SecureExtension3.groovy
// disallow calls on System
onMethodSelection { expr, methodNode ->
    if (methodNode.declaringClass.name=='java.lang.System') {
        addStaticTypeError("Method call is not allowed!", expr)
    }
}

// resolve the score variable
unresolvedVariable { var ->
    if (var.name=='score') {
        return makeDynamic(var, double_TYPE)
    }
}

Don’t forget to update the configuration in your Java class to use the new type checking extension:

ASTTransformationCustomizer astcz = new ASTTransformationCustomizer(
        singletonMap("extensions", singletonList("SecureExtension3.groovy")),
	CompileStatic.class);

Execute the code again and it still works. Now, try to do:

abs(cos(1+score))
System.exit(-1)

And the script compilation will fail with:

Script1.groovy: 1: [Static type checking] - Method call is not allowed!
 @ line 1, column 19.
   abs(cos(1+score));System.exit(-1)
                     ^

1 error

Congratulations, you just wrote your first type checking extension that prevents the execution of malicious code!

Improving configuration of the extension

So far so good, we are able to prevent calls on System, but it is likely that we are going to discover new vulnerabilities, and that we will want to prevent execution of such code. So instead of hardcoding everything in the extension, we will try to make our extension generic and configurable. This is probably the trickiest thing to do, because there’s no direct way to provide context to a type checking extension. Our idea therefore relies on the (ugly) thread locals to pass configuration data to the type checker.

The first thing we’re going to do is to make the variable list configurable. Here is the code on the Java side of things:

Sandbox.java
public class Sandbox {
    public static final String VAR_TYPES = "sandboxing.variable.types";

    public static final ThreadLocal<Map<String, Object>> COMPILE_OPTIONS = new ThreadLocal<>();		(1)

    public static void main(String[] args) {
        CompilerConfiguration conf = new CompilerConfiguration();
        ImportCustomizer customizer = new ImportCustomizer();
        customizer.addStaticStars("java.lang.Math");
        ASTTransformationCustomizer astcz = new ASTTransformationCustomizer(
                singletonMap("extensions", singletonList("SecureExtension4.groovy")),			(2)
                CompileStatic.class);
        conf.addCompilationCustomizers(astcz);
        conf.addCompilationCustomizers(customizer);

        Binding binding = new Binding();
        binding.setVariable("score", 2.0d);
        try {
            Map<String,ClassNode> variableTypes = new HashMap<String, ClassNode>();			(3)
            variableTypes.put("score", ClassHelper.double_TYPE);					(4)
            Map<String,Object> options = new HashMap<String, Object>();					(5)
            options.put(VAR_TYPES, variableTypes);							(6)
            COMPILE_OPTIONS.set(options);								(7)
            GroovyShell shell = new GroovyShell(binding, conf);
            Double userScore = (Double) shell.evaluate("abs(cos(1+score));System.exit(-1)");
            System.out.println("userScore = " + userScore);
        } finally {
            COMPILE_OPTIONS.remove();									(8)
        }
    }
}
1 create a ThreadLocal that will hold the contextual configuration of the type checking extension
2 update the extension to SecureExtension4.groovy
3 variableTypes is a map variable name → variable type
4 so this is where we’re going to add the score variable declaration
5 options is the map that will store our type checking configuration
6 we set the "variable types" value of this configuration map to the map of variable types
7 and assign it to the thread local
8 eventually, to avoid memory leaks, it is important to remove the configuration from the thread local

And now, here is how the type checking extension can use this:

import static Sandbox.*

def typesOfVariables = COMPILE_OPTIONS.get()[VAR_TYPES]				(1)

unresolvedVariable { var ->
    if (typesOfVariables[var.name]) {						(2)
        return makeDynamic(var, typesOfVariables[var.name])			(3)
    }
}
1 Retrieve the list of variable types from the thread local
2 if an unresolved variable is found in the map of known variables
3 then declare to the type checker that the variable is of the type found in the map

Basically, the type checking extension, because it is executed when the type checker verifies the script, can access the configuration through the thread local. Then, instead of using hard coded names in unresolvedVariable, we can just check that the variable that the type checker doesn’t know about is actually declared in the configuration. If it is, then we can tell it which type it is. Easy!

Now we have to find a way to explicitly declare the list of allowed method calls. It is a bit trickier to find a proper configuration for that, but here is what we came up with.

Configuring a white list of methods

The idea of the whitelist is simple. A method call will be allowed if the method descriptor can be found in the whitelist. This whitelist consists of regular expressions, and the method descriptor consists of the fully-qualified class name of the method, it’s name and parameters. For example, for System.exit, the descriptor would be:

java.lang.System#exit(int)

So let’s see how to update the Java integration part to add this configuration:

public class Sandbox {
    public static final String WHITELIST_PATTERNS = "sandboxing.whitelist.patterns";

    // ...

    public static void main(String[] args) {
        // ...
        try {
            Map<String,ClassNode> variableTypes = new HashMap<String, ClassNode>();
            variableTypes.put("score", ClassHelper.double_TYPE);
            Map<String,Object> options = new HashMap<String, Object>();
            List<String> patterns = new ArrayList<String>();					(1)
            patterns.add("java\\.lang\\.Math#");						(2)
            options.put(VAR_TYPES, variableTypes);
            options.put(WHITELIST_PATTERNS, patterns);						(3)
            COMPILE_OPTIONS.set(options);
            GroovyShell shell = new GroovyShell(binding, conf);
            Double userScore = (Double) shell.evaluate("abs(cos(1+score));System.exit(-1)");
            System.out.println("userScore = " + userScore);
        } finally {
            COMPILE_OPTIONS.remove();
        }
    }
}
1 declare a list of patterns
2 add all methods of java.lang.Math as allowed
3 put the whitelist to the type checking options map

Then on the type checking extension side:

import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.transform.stc.ExtensionMethodNode

import static Sandbox.*

@CompileStatic
private static String prettyPrint(ClassNode node) {
    node.isArray()?"${prettyPrint(node.componentType)}[]":node.toString(false)
}

@CompileStatic
private static String toMethodDescriptor(MethodNode node) {								(1)
    if (node instanceof ExtensionMethodNode) {
        return toMethodDescriptor(node.extensionMethodNode)
    }
    def sb = new StringBuilder()
    sb.append(node.declaringClass.toString(false))
    sb.append("#")
    sb.append(node.name)
    sb.append('(')
    sb.append(node.parameters.collect { Parameter it ->
        prettyPrint(it.originType)
    }.join(','))
    sb.append(')')
    sb
}
def typesOfVariables = COMPILE_OPTIONS.get()[VAR_TYPES]
def whiteList = COMPILE_OPTIONS.get()[WHITELIST_PATTERNS]								(2)

onMethodSelection { expr, MethodNode methodNode ->
    def descr = toMethodDescriptor(methodNode)										(3)
    if (!whiteList.any { descr =~ it }) {										(4)
        addStaticTypeError("You tried to call a method which is not allowed, what did you expect?: $descr", expr)	(5)
    }
}

unresolvedVariable { var ->
    if (typesOfVariables[var.name]) {
        return makeDynamic(var, typesOfVariables[var.name])
    }
}
1 this method will generate a method descriptor from a MethodNode
2 retrieve the whitelist of methods from the thread local option map
3 convert a selected method into a descriptor string
4 if the descriptor doesn’t match any of the whitelist entries, throw an error

So if you execute the code again, you will now have a very cool error:

Script1.groovy: 1: [Static type checking] - You tried to call a method which is not allowed, what did you expect?: java.lang.System#exit(int)
 @ line 1, column 19.
   abs(cos(1+score));System.exit(-1)
                     ^

1 error

There we are! We now have a type checking extension which handles both the types of the variables that you export in the binding and a whitelist of allowed methods. This is still not perfect, but we’re very close to the final solution! It’s not perfect because we only took care of method calls here, but you have to deal with more than that. For example, properties (like foo.text which is implicitly converted into foo.getText()).

Putting it altogether

Dealing with properties is a bit more complicated because the type checker doesn’t have a handler for "property selection" like it does for methods. We can work around that, and if you are interested in seeing the resulting code, check it out below. It’s a type checking extension which is not written exactly as you have seen in this blog post, because it is meant to be precompiled for improved performance. But the idea is exactly the same.

SandboxingTypeCheckingExtension.groovy
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassCodeVisitorSupport
import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.ast.expr.PropertyExpression
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys
import org.codehaus.groovy.transform.stc.ExtensionMethodNode
import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport
import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport

import static Sandbox.*

class SandboxingTypeCheckingExtension extends GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {

    @CompileStatic
    private static String prettyPrint(ClassNode node) {
        node.isArray()?"${prettyPrint(node.componentType)}[]":node.toString(false)
    }

    @CompileStatic
    private static String toMethodDescriptor(MethodNode node) {
        if (node instanceof ExtensionMethodNode) {
            return toMethodDescriptor(node.extensionMethodNode)
        }
        def sb = new StringBuilder()
        sb.append(node.declaringClass.toString(false))
        sb.append("#")
        sb.append(node.name)
        sb.append('(')
        sb.append(node.parameters.collect { Parameter it ->
            prettyPrint(it.originType)
        }.join(','))
        sb.append(')')
        sb
    }

    @Override
    Object run() {

        // Fetch white list of regular expressions of authorized method calls
        def whiteList = COMPILE_OPTIONS.get()[WHITELIST_PATTERNS]
        def typesOfVariables = COMPILE_OPTIONS.get()[VAR_TYPES]

        onMethodSelection { expr, MethodNode methodNode ->
            def descr = toMethodDescriptor(methodNode)
            if (!whiteList.any { descr =~ it }) {
                addStaticTypeError("You tried to call a method which is not allowed, what did you expect?: $descr", expr)
            }
        }

        unresolvedVariable { var ->
            if (isDynamic(var) && typesOfVariables[var.name]) {
                storeType(var, typesOfVariables[var.name])
                handled = true
            }
        }

        // handling properties (like foo.text) is harder because the type checking extension
        // does not provide a specific hook for this. Harder, but not impossible!

        afterVisitMethod { methodNode ->
            def visitor = new PropertyExpressionChecker(context.source, whiteList)
            visitor.visitMethod(methodNode)
        }
    }

    private class PropertyExpressionChecker extends ClassCodeVisitorSupport {
        private final SourceUnit unit
        private final List<String> whiteList

        PropertyExpressionChecker(final SourceUnit unit, final List<String> whiteList) {
            this.unit = unit
            this.whiteList = whiteList
        }

        @Override
        protected SourceUnit getSourceUnit() {
            unit
        }

        @Override
        void visitPropertyExpression(final PropertyExpression expression) {
            super.visitPropertyExpression(expression)

            ClassNode owner = expression.objectExpression.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER)
            if (owner) {
                if (expression.spreadSafe && StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(owner, classNodeFor(Collection))) {
                    owner = typeCheckingVisitor.inferComponentType(owner, ClassHelper.int_TYPE)
                }
                def descr = "${prettyPrint(owner)}#${expression.propertyAsString}"
                if (!whiteList.any { descr =~ it }) {
                    addStaticTypeError("Property is not allowed: $descr", expression)
                }
            }
        }
    }
}

And a final version of the sandbox that includes assertions to make sure that we catch all cases:

Sandbox.java
public class Sandbox {
    public static final String WHITELIST_PATTERNS = "sandboxing.whitelist.patterns";
    public static final String VAR_TYPES = "sandboxing.variable.types";

    public static final ThreadLocal<Map<String, Object>> COMPILE_OPTIONS = new ThreadLocal<Map<String, Object>>();

    public static void main(String[] args) {
        CompilerConfiguration conf = new CompilerConfiguration();
        ImportCustomizer customizer = new ImportCustomizer();
        customizer.addStaticStars("java.lang.Math");
        ASTTransformationCustomizer astcz = new ASTTransformationCustomizer(
                singletonMap("extensions", singletonList("SandboxingTypeCheckingExtension.groovy")),
                CompileStatic.class);
        conf.addCompilationCustomizers(astcz);
        conf.addCompilationCustomizers(customizer);

        Binding binding = new Binding();
        binding.setVariable("score", 2.0d);
        try {
            Map<String, ClassNode> variableTypes = new HashMap<String, ClassNode>();
            variableTypes.put("score", ClassHelper.double_TYPE);
            Map<String, Object> options = new HashMap<String, Object>();
            List<String> patterns = new ArrayList<String>();
            // allow method calls on Math
            patterns.add("java\\.lang\\.Math#");
            // allow constructors calls on File
            patterns.add("File#<init>");
            // because we let the user call each/times/...
            patterns.add("org\\.codehaus\\.groovy\\.runtime\\.DefaultGroovyMethods");
            options.put(VAR_TYPES, variableTypes);
            options.put(WHITELIST_PATTERNS, patterns);
            COMPILE_OPTIONS.set(options);
            GroovyShell shell = new GroovyShell(binding, conf);
            Object result;
            try {
                result = shell.evaluate("Eval.me('1')"); // error
                assert false;
            } catch (MultipleCompilationErrorsException e) {
                System.out.println("Successful sandboxing: "+e.getMessage());
            }
            try {
                result = shell.evaluate("System.exit(-1)"); // error
                assert false;
            } catch (MultipleCompilationErrorsException e) {
                System.out.println("Successful sandboxing: "+e.getMessage());
            }
            try {
                result = shell.evaluate("((Object)Eval).me('1')"); // error
                assert false;
            } catch (MultipleCompilationErrorsException e) {
                System.out.println("Successful sandboxing: "+e.getMessage());
            }

            try {
                result = shell.evaluate("new File('/etc/passwd').getText()"); // getText is not allowed
                assert false;
            } catch (MultipleCompilationErrorsException e) {
                System.out.println("Successful sandboxing: "+e.getMessage());
            }

            try {
                result = shell.evaluate("new File('/etc/passwd').text");  // getText is not allowed
                assert false;
            } catch (MultipleCompilationErrorsException e) {
                System.out.println("Successful sandboxing: "+e.getMessage());
            }

            Double userScore = (Double) shell.evaluate("abs(cos(1+score))");
            System.out.println("userScore = " + userScore);
        } finally {
            COMPILE_OPTIONS.remove();
        }
    }
}

Conclusion

This post has explored the interest of using Groovy as a platform for scripting on the JVM. It introduced various mechanisms for integration, and showed that this comes at the price of security. However, we illustrated some concepts like compilation customizers that make it easier to sandbox the environment of execution of scripts. The current list of customizers available in the Groovy distribution, and the currently available sandboxing projects in the wild, are not sufficient to guarantee security of execution of scripts in the general case (dependending, of course, on your users and where the scripts come from).

We then illustrated how you could, if you are ready to pay the price of loosing some of the dynamic features of the language, properly workaround those limitations through type checking extensions. Those type checking extensions are so powerful that you can even introduce your own error messages during the compilation of scripts. Eventually, by doing this and caching your scripts, you will also benefit from dramatic performance improvements in script execution.

Eventually, the sandboxing mechanism that we illustrated is not a replacement for the SecureASTCustomizer. We recommand that you actually use both, because they work on different levels: the secure AST customizer will work on the grammar level, allowing you to restrict some language constructs (for example preventing the creation of closures or classes inside the script), while the type checking extension will work after type inference (allowing you to reason on inferred types rather than declared types).

Last but not least, the solution that I described here is incomplete. It is not available in Groovy core. As I will have less time to work on Groovy, I would be very glad if someone or some people improve this solution and make a pull request so that we can have something!

Comments


Older posts are available in the archive.